Elixir - 在各种 if 子句中附加到元组,但随后元组不断重置为空白
Elixir - Append to tuple in various if clauses, but then tuple keeps getting reset to blank
此代码曾经有效,能够根据各种子句将自定义消息放入 messages
并将这些消息发送回前端。
我更新了 Elixir。现在 messages
在发送回前端时总是 {}
。我需要做什么才能让这个旧代码现在将消息附加到 messages
?它会附加它们,然后我会立即检查,它们会在那里。但最后,里面什么也没有。
我的所有其他代码仍然有效,只是 messages
不再向前端返回任何内容,因为它在函数结束时变为空。这就像 iff 中的变量范围不一样,它是一个完全不同的变量或其他东西。
defmodule Api.Controllers.PutProductIsVegan do
import Api.UserProduct
alias Api.UserProduct, as: UserProduct
import Ecto.Query
import Api.Repo
import Plug.Conn
def parse_elem("true"), do: true
def parse_elem("false"), do: false
def put_product_is_vegan(conn) do
messages = {}
db_user_product = %{}
product = Api.Product |> Api.Repo.get(conn.query_params["p_id"])
vegan_count = product.vegan_count
not_vegan_count = product.not_vegan_count
confirm = parse_elem(conn.body_params["confirm"])
report = parse_elem(conn.body_params["report"])
IO.inspect(confirm, label: "confirm")
IO.inspect(report, label: "report")
uid = conn.query_params["u_id"]
pid = conn.query_params["p_id"]
IO.inspect(uid, label: "confirm")
IO.inspect(pid, label: "report")
user_product = (from up in Api.UserProduct,
where: up.u_id == ^uid,
where: up.p_id == ^pid)
|> Api.Repo.one
IO.inspect(user_product)
if !user_product do
IO.puts("insert user product")
UserProduct.insert_user_product(conn, %{
p_id: String.to_integer(pid),
u_id: uid,
voted_not_vegan: report,
voted_vegan: confirm
})
end
user_product = (from up in Api.UserProduct,
where: up.u_id == ^uid,
where: up.p_id == ^pid)
|> Api.Repo.one
IO.inspect(user_product)
if !user_product.voted_not_vegan && report do
IO.puts("add 1 to product.not_vegan_count")
changeset = Api.Product.changeset(
product, %{:not_vegan_count => not_vegan_count + 1}
)
case Api.Repo.update(changeset) do
{:ok, product} ->
messages = Tuple.append(messages, "Product updated")
{:error, changeset} ->
messages = Tuple.append(messages, "Product not updated")
end
IO.puts("set up.voted_not_vegan to true")
changeset = Api.UserProduct.changeset(
user_product, %{:voted_not_vegan => true}
)
case Api.Repo.update(changeset) do
{:ok, product} ->
messages = Tuple.append(messages, "Product updated")
{:error, changeset} ->
messages = Tuple.append(messages, "Product not updated")
end
end
if !user_product.voted_vegan && confirm do
IO.puts("add 1 to product.vegan_count")
changeset = Api.Product.changeset(
product, %{:vegan_count => vegan_count + 1}
)
case Api.Repo.update(changeset) do
{:ok, product} ->
messages = Tuple.append(messages, "Product updated")
{:error, changeset} ->
messages = Tuple.append(messages, "Product not updated")
end
IO.puts("set up.voted_vegan to true")
IO.inspect (user_product)
changeset = Api.UserProduct.changeset(
user_product, %{:voted_vegan => true}
)
case Api.Repo.update(changeset) do
{:ok, product} ->
messages = Tuple.append(messages, "Product updated")
{:error, changeset} ->
messages = Tuple.append(messages, "Product not updated")
end
end
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: "success",
errors: Tuple.to_list(messages)
}))
end
end
编辑
根据 Adams 的回答,我已经这样做了,但是 messages
最后仍然是空白:
defmodule Api.Controllers.PutProductIsVegan do
import Api.UserProduct
alias Api.UserProduct, as: UserProduct
import Ecto.Query
import Api.Repo
import Plug.Conn
def parse_elem("true"), do: true
def parse_elem("false"), do: false
def put_product_is_vegan(conn) do
messages = {}
db_user_product = %{}
product = Api.Product |> Api.Repo.get(conn.query_params["p_id"])
vegan_count = product.vegan_count
not_vegan_count = product.not_vegan_count
confirm = parse_elem(conn.body_params["confirm"])
report = parse_elem(conn.body_params["report"])
IO.inspect(confirm, label: "confirm")
IO.inspect(report, label: "report")
uid = conn.query_params["u_id"]
pid = conn.query_params["p_id"]
IO.inspect(uid, label: "confirm")
IO.inspect(pid, label: "report")
user_product = (from up in Api.UserProduct,
where: up.u_id == ^uid,
where: up.p_id == ^pid)
|> Api.Repo.one
IO.inspect(user_product)
if !user_product do
IO.puts("insert user product")
UserProduct.insert_user_product(conn, %{
p_id: String.to_integer(pid),
u_id: uid,
voted_not_vegan: report,
voted_vegan: confirm
})
end
user_product = (from up in Api.UserProduct,
where: up.u_id == ^uid,
where: up.p_id == ^pid)
|> Api.Repo.one
IO.inspect(user_product)
if user_product.voted_not_vegan && report do
messages = Tuple.append(messages, "You have already reported this product")
end
if !user_product.voted_not_vegan && report do
IO.puts("add 1 to product.not_vegan_count")
changeset = Api.Product.changeset(
product, %{:not_vegan_count => not_vegan_count + 1}
)
messages =
case Api.Repo.update(changeset) do
{:ok, product} -> Tuple.append(messages, "Product updated")
{:error, changeset} -> Tuple.append(messages, "Product not updated")
end
IO.puts("set up.voted_not_vegan to true")
changeset = Api.UserProduct.changeset(
user_product, %{:voted_not_vegan => true}
)
messages =
case Api.Repo.update(changeset) do
{:ok, product} -> Tuple.append(messages, "Product updated")
{:error, changeset} -> Tuple.append(messages, "Product not updated")
end
end
if !user_product.voted_vegan && confirm do
IO.puts("add 1 to product.vegan_count")
changeset = Api.Product.changeset(
product, %{:vegan_count => vegan_count + 1}
)
messages =
case Api.Repo.update(changeset) do
{:ok, product} -> Tuple.append(messages, "Product updated")
{:error, changeset} -> Tuple.append(messages, "Product not updated")
end
IO.puts("set up.voted_vegan to true")
IO.inspect (user_product)
changeset = Api.UserProduct.changeset(
user_product, %{:voted_vegan => true}
)
messages =
case Api.Repo.update(changeset) do
{:ok, product} -> Tuple.append(messages, "Product updated")
{:error, changeset} -> Tuple.append(messages, "Product not updated")
end
end
IO.inspect(messages) # this is {}
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: "success",
errors: Tuple.to_list(messages)
}))
end
end
你的问题是在这样的语句中:
case Api.Repo.update(changeset) do
{:ok, product} ->
messages = Tuple.append(messages, "Product updated")
{:error, changeset} ->
messages = Tuple.append(messages, "Product not updated")
end
虽然您正在分配给 messages
,但它的范围是在 case 语句内部。您有效地附加到元组,将其分配给一个新的范围变量,该变量也恰好称为 messages
,然后丢弃它。然后,当您执行 Tuple.to_list(messages)
.
时,您将使用 messages = {}
的原始声明
您应该可以通过将 case 语句的结果分配给 messages
:
来修复它
messages =
case Api.Repo.update(changeset) do
{:ok, product} -> Tuple.append(messages, "Product updated")
{:error, changeset} -> Tuple.append(messages, "Product not updated")
end
不幸的是,您似乎需要进行大量重组,因为代码使用的是不可变语言中的可变编码风格。如果你想重组,你可以这样做:
定义一个接受消息列表并有条件地添加新消息的函数:
def add_message_if(messages, message, true), do: [message | messages]
def add_message_if(messages, _message, false), do: messages
然后你可以像这样在链中调用它:
messages =
[]
|> add_message_if("You have already reported this product", user_product.voted_not_vegan)
|> add_message_if("Product updated", !user_product.voted_not_vegan && updated?)
|> add_message_if("Product not updated", !user_product.voted_not_vegan && !updated?)
...
最后,Enum.reverse(messages)
,因为我们在消息前面。
您应该首先了解 Elixir 中的一切 是不可变的。 一切意味着一切。以下代码
foo = {}
if true, do: foo = Tuple.append(foo, :bar)
不会像您预期的那样更改外部 foo
。也就是说,初始赋值 messages = {}
是零意义的,如果条件语句实际上都是 NOOP,那么 wall.
此外,不应该使用元组来减少,如果所需的结果类型是元组,最好 Enum.reduce/3
使用列表累加器然后转换为元组。有点像这样:
actions = %{
user_product.voted_not_vegan && report => fn messages ->
["You have already reported this product" | messages]
end,
!user_product.voted_not_vegan && report => fn messages ->
IO.puts("add 1 to product.not_vegan_count")
changesets = [
Api.Product.changeset(
product, %{not_vegan_count: not_vegan_count + 1}
),
Api.UserProduct.changeset(
user_product, %{:voted_not_vegan => true}
)
]
Enum.reduce(changesets, messages, fn
{:ok, _} -> ["Product updated" | messages]
{:error, _} -> ["Product not updated" | messages]
end)
end,
...
}
actions
|> Enum.reduce([], fn
{true, reducer}, messages -> reducer.(messages)
{false, _} -> messages
end)
|> Enum.reverse()
|> List.to_tuple()
这里我们首先构建 action_needed => action
的地图,然后减少操作,仅在需要时应用相应的操作。
因为我们使用 reduce
,所以我们在迭代之间保留累加器值,不像在任何地方重新分配它。
有一条经验法则:在 Elixir 中 99% 的情况下,如果您发现自己使用了条件语句,那您就完全错了。
此代码曾经有效,能够根据各种子句将自定义消息放入 messages
并将这些消息发送回前端。
我更新了 Elixir。现在 messages
在发送回前端时总是 {}
。我需要做什么才能让这个旧代码现在将消息附加到 messages
?它会附加它们,然后我会立即检查,它们会在那里。但最后,里面什么也没有。
我的所有其他代码仍然有效,只是 messages
不再向前端返回任何内容,因为它在函数结束时变为空。这就像 iff 中的变量范围不一样,它是一个完全不同的变量或其他东西。
defmodule Api.Controllers.PutProductIsVegan do
import Api.UserProduct
alias Api.UserProduct, as: UserProduct
import Ecto.Query
import Api.Repo
import Plug.Conn
def parse_elem("true"), do: true
def parse_elem("false"), do: false
def put_product_is_vegan(conn) do
messages = {}
db_user_product = %{}
product = Api.Product |> Api.Repo.get(conn.query_params["p_id"])
vegan_count = product.vegan_count
not_vegan_count = product.not_vegan_count
confirm = parse_elem(conn.body_params["confirm"])
report = parse_elem(conn.body_params["report"])
IO.inspect(confirm, label: "confirm")
IO.inspect(report, label: "report")
uid = conn.query_params["u_id"]
pid = conn.query_params["p_id"]
IO.inspect(uid, label: "confirm")
IO.inspect(pid, label: "report")
user_product = (from up in Api.UserProduct,
where: up.u_id == ^uid,
where: up.p_id == ^pid)
|> Api.Repo.one
IO.inspect(user_product)
if !user_product do
IO.puts("insert user product")
UserProduct.insert_user_product(conn, %{
p_id: String.to_integer(pid),
u_id: uid,
voted_not_vegan: report,
voted_vegan: confirm
})
end
user_product = (from up in Api.UserProduct,
where: up.u_id == ^uid,
where: up.p_id == ^pid)
|> Api.Repo.one
IO.inspect(user_product)
if !user_product.voted_not_vegan && report do
IO.puts("add 1 to product.not_vegan_count")
changeset = Api.Product.changeset(
product, %{:not_vegan_count => not_vegan_count + 1}
)
case Api.Repo.update(changeset) do
{:ok, product} ->
messages = Tuple.append(messages, "Product updated")
{:error, changeset} ->
messages = Tuple.append(messages, "Product not updated")
end
IO.puts("set up.voted_not_vegan to true")
changeset = Api.UserProduct.changeset(
user_product, %{:voted_not_vegan => true}
)
case Api.Repo.update(changeset) do
{:ok, product} ->
messages = Tuple.append(messages, "Product updated")
{:error, changeset} ->
messages = Tuple.append(messages, "Product not updated")
end
end
if !user_product.voted_vegan && confirm do
IO.puts("add 1 to product.vegan_count")
changeset = Api.Product.changeset(
product, %{:vegan_count => vegan_count + 1}
)
case Api.Repo.update(changeset) do
{:ok, product} ->
messages = Tuple.append(messages, "Product updated")
{:error, changeset} ->
messages = Tuple.append(messages, "Product not updated")
end
IO.puts("set up.voted_vegan to true")
IO.inspect (user_product)
changeset = Api.UserProduct.changeset(
user_product, %{:voted_vegan => true}
)
case Api.Repo.update(changeset) do
{:ok, product} ->
messages = Tuple.append(messages, "Product updated")
{:error, changeset} ->
messages = Tuple.append(messages, "Product not updated")
end
end
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: "success",
errors: Tuple.to_list(messages)
}))
end
end
编辑
根据 Adams 的回答,我已经这样做了,但是 messages
最后仍然是空白:
defmodule Api.Controllers.PutProductIsVegan do
import Api.UserProduct
alias Api.UserProduct, as: UserProduct
import Ecto.Query
import Api.Repo
import Plug.Conn
def parse_elem("true"), do: true
def parse_elem("false"), do: false
def put_product_is_vegan(conn) do
messages = {}
db_user_product = %{}
product = Api.Product |> Api.Repo.get(conn.query_params["p_id"])
vegan_count = product.vegan_count
not_vegan_count = product.not_vegan_count
confirm = parse_elem(conn.body_params["confirm"])
report = parse_elem(conn.body_params["report"])
IO.inspect(confirm, label: "confirm")
IO.inspect(report, label: "report")
uid = conn.query_params["u_id"]
pid = conn.query_params["p_id"]
IO.inspect(uid, label: "confirm")
IO.inspect(pid, label: "report")
user_product = (from up in Api.UserProduct,
where: up.u_id == ^uid,
where: up.p_id == ^pid)
|> Api.Repo.one
IO.inspect(user_product)
if !user_product do
IO.puts("insert user product")
UserProduct.insert_user_product(conn, %{
p_id: String.to_integer(pid),
u_id: uid,
voted_not_vegan: report,
voted_vegan: confirm
})
end
user_product = (from up in Api.UserProduct,
where: up.u_id == ^uid,
where: up.p_id == ^pid)
|> Api.Repo.one
IO.inspect(user_product)
if user_product.voted_not_vegan && report do
messages = Tuple.append(messages, "You have already reported this product")
end
if !user_product.voted_not_vegan && report do
IO.puts("add 1 to product.not_vegan_count")
changeset = Api.Product.changeset(
product, %{:not_vegan_count => not_vegan_count + 1}
)
messages =
case Api.Repo.update(changeset) do
{:ok, product} -> Tuple.append(messages, "Product updated")
{:error, changeset} -> Tuple.append(messages, "Product not updated")
end
IO.puts("set up.voted_not_vegan to true")
changeset = Api.UserProduct.changeset(
user_product, %{:voted_not_vegan => true}
)
messages =
case Api.Repo.update(changeset) do
{:ok, product} -> Tuple.append(messages, "Product updated")
{:error, changeset} -> Tuple.append(messages, "Product not updated")
end
end
if !user_product.voted_vegan && confirm do
IO.puts("add 1 to product.vegan_count")
changeset = Api.Product.changeset(
product, %{:vegan_count => vegan_count + 1}
)
messages =
case Api.Repo.update(changeset) do
{:ok, product} -> Tuple.append(messages, "Product updated")
{:error, changeset} -> Tuple.append(messages, "Product not updated")
end
IO.puts("set up.voted_vegan to true")
IO.inspect (user_product)
changeset = Api.UserProduct.changeset(
user_product, %{:voted_vegan => true}
)
messages =
case Api.Repo.update(changeset) do
{:ok, product} -> Tuple.append(messages, "Product updated")
{:error, changeset} -> Tuple.append(messages, "Product not updated")
end
end
IO.inspect(messages) # this is {}
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: "success",
errors: Tuple.to_list(messages)
}))
end
end
你的问题是在这样的语句中:
case Api.Repo.update(changeset) do
{:ok, product} ->
messages = Tuple.append(messages, "Product updated")
{:error, changeset} ->
messages = Tuple.append(messages, "Product not updated")
end
虽然您正在分配给 messages
,但它的范围是在 case 语句内部。您有效地附加到元组,将其分配给一个新的范围变量,该变量也恰好称为 messages
,然后丢弃它。然后,当您执行 Tuple.to_list(messages)
.
messages = {}
的原始声明
您应该可以通过将 case 语句的结果分配给 messages
:
messages =
case Api.Repo.update(changeset) do
{:ok, product} -> Tuple.append(messages, "Product updated")
{:error, changeset} -> Tuple.append(messages, "Product not updated")
end
不幸的是,您似乎需要进行大量重组,因为代码使用的是不可变语言中的可变编码风格。如果你想重组,你可以这样做:
定义一个接受消息列表并有条件地添加新消息的函数:
def add_message_if(messages, message, true), do: [message | messages]
def add_message_if(messages, _message, false), do: messages
然后你可以像这样在链中调用它:
messages =
[]
|> add_message_if("You have already reported this product", user_product.voted_not_vegan)
|> add_message_if("Product updated", !user_product.voted_not_vegan && updated?)
|> add_message_if("Product not updated", !user_product.voted_not_vegan && !updated?)
...
最后,Enum.reverse(messages)
,因为我们在消息前面。
您应该首先了解 Elixir 中的一切 是不可变的。 一切意味着一切。以下代码
foo = {}
if true, do: foo = Tuple.append(foo, :bar)
不会像您预期的那样更改外部 foo
。也就是说,初始赋值 messages = {}
是零意义的,如果条件语句实际上都是 NOOP,那么 wall.
此外,不应该使用元组来减少,如果所需的结果类型是元组,最好 Enum.reduce/3
使用列表累加器然后转换为元组。有点像这样:
actions = %{
user_product.voted_not_vegan && report => fn messages ->
["You have already reported this product" | messages]
end,
!user_product.voted_not_vegan && report => fn messages ->
IO.puts("add 1 to product.not_vegan_count")
changesets = [
Api.Product.changeset(
product, %{not_vegan_count: not_vegan_count + 1}
),
Api.UserProduct.changeset(
user_product, %{:voted_not_vegan => true}
)
]
Enum.reduce(changesets, messages, fn
{:ok, _} -> ["Product updated" | messages]
{:error, _} -> ["Product not updated" | messages]
end)
end,
...
}
actions
|> Enum.reduce([], fn
{true, reducer}, messages -> reducer.(messages)
{false, _} -> messages
end)
|> Enum.reverse()
|> List.to_tuple()
这里我们首先构建 action_needed => action
的地图,然后减少操作,仅在需要时应用相应的操作。
因为我们使用 reduce
,所以我们在迭代之间保留累加器值,不像在任何地方重新分配它。
有一条经验法则:在 Elixir 中 99% 的情况下,如果您发现自己使用了条件语句,那您就完全错了。