Elixir:如何处理函数中的可选/默认参数和nil值?

Elixir:如何处理函数中的可选/默认参数和nil值?,elixir,Elixir,我有一个关于默认参数和nil值的一般性问题。假设我有两个函数。一个调用另一个(这是一个助手函数)。两者都有一个可选参数 helper函数只是用一个连接符将一个列表连接到一个字符串。joiner被传递给opts关键字列表中的第一个函数。joiner的传递是可选的,它默认为和“ 问题是:在第二个示例中,opts[:joiner]将为零。但它仍然存在,因此不会使用默认值 一种可能的解决方案是使用案例: defmodule ParamTest do def func_1(list, opts \\

我有一个关于默认参数和nil值的一般性问题。假设我有两个函数。一个调用另一个(这是一个助手函数)。两者都有一个可选参数

helper函数只是用一个连接符将一个列表连接到一个字符串。joiner被传递给opts关键字列表中的第一个函数。joiner的传递是可选的,它默认为
和“

问题是:在第二个示例中,opts[:joiner]将为零。但它仍然存在,因此不会使用默认值

一种可能的解决方案是使用
案例

defmodule ParamTest do
  def func_1(list, opts \\ []) do
    case is_nil(opts[:joiner]) do
      true -> helper(list)
      false -> helper(list, opts[:joiner])
    end
    # Do something else with the result
  end

  defp helper(list, joiner \\ "AND") do
    Enum.join(list, " #{joiner} ")
  end
end
另一种方法是对helper使用两个函数定义,并使用模式匹配:

defmodule ParamTest do
  def func_1(list, opts \\ []) do
    case is_nil(opts[:joiner]) do
      true -> helper(list)
      false -> helper(list, opts[:joiner])
    end
  end

  defp helper(list, nil) do
    Enum.join(list, " AND ")
  end

  defp helper(list, joiner \\ "AND") do
    Enum.join(list, " #{joiner} ")
  end
end
但我觉得这不是很优雅,在更复杂的情况下可能会变得混乱


对于这种情况,有什么更好的解决方案?

在我看来,没有比您已有的更好的解决方案了。就我个人而言,我会问自己以下问题:

  • 我确定需要
    helper/2
    private函数的默认参数吗?我对此没有信心,但我觉得默认的
    \\\
    参数到私有函数可能是某种代码味道
  • 如果使用
    \\
    默认参数,我想在哪里处理复杂性?:)
如果必须选择,在这种特殊情况下,我可能会根据
:joiner
选项的存在,分别调用
helper/1
helper/2

defmodule ParamTest do
  def func_1(list, opts \\ []) do
    if joiner = opts[:joiner] do
      helper(list, joiner)
    else
      helper(list)
    end
  end

  defp helper(list, joiner \\ "AND") do
    Enum.join(list, " #{joiner} ")
  end
end
然而,正如我上面所说的,由于
helper/2
是一个私有函数,因此通过使用选项的默认值,将可选的接合器完全移动到“系统”的边界(即仅移动到
func_1/2
)可能是有意义的(取决于您的用例,这个太小了,我们无法做出更深思熟虑的决定:p):

defmodule ParamTest do
  def func_1(list, opts \\ []) do
    helper(list, opts[:joiner] || "AND")
  end

  defp helper(list, joiner) do
    Enum.join(list, " #{joiner} ")
  end
end

同样,这在您的用例中可能无法很好地扩展,但我觉得这是我们从问题中获得的信息所能做的最好的:)。

最好的解决方案是在helper中强制使用joiner,并在
func\u 1
中提供默认选项

def func_1(list, opts \\ [joiner: "AND"]) do
  helper(list, opts[:joiner])
  ...
end
defp helper(list, joiner) do
  ...
end
总是试着把你的顾虑分开<代码>帮助程序不是公共API的一部分,因此您可以始终传递所有选项。让它只做它的工作,而不用担心默认值

func_1
是您的公共API,它应该担心默认设置。您希望在默认情况下指定“和”joiner,所以请这样做,而不是默认为空选项列表。当开发人员阅读您的代码时,他们不需要深入检查“AND”来自何处,并且可以很容易地确定,他们可以在不阅读文档甚至函数体的情况下传递此选项


在顶级函数(API)中使用默认值通常是一个好主意,只是为了方便起见,并且只需显式地向下传递所有内容。否则,您必须在每个级别进行检查,如果该选项像您在示例中使用
case
时那样被传递。这很容易出错。

我同意私有函数的默认参数。我喜欢你的第二个解决方案。谢谢。如果只从第一个函数调用helper,那么使用双默认值是很尴尬的。第二种解决方案很尴尬,因为函数调用中有| |。您最好将默认值设置为opts\\\[joiner:”和“]@CaptChrisD,正如其他人在对另一个函数的评论中指出的那样,
[joiner:”和“]
作为默认值通常不是一个好主意,因为该函数可以接受多个选项(很少函数只接受一个选项)。我甚至比@whatyouhide更喜欢你的版本,因为它让第一次阅读我代码的人非常清楚默认版本。谢谢,我不得不收回我先前的声明。随着opts参数传入更多选项,所有内容都将被覆盖,此解决方案将不再有效
[“el 1”,“el 2”]|>parametest.func_1(某物:“任何”)
将生成
“el 1 el 2”
您是对的。如果您有更多选项,并且只想覆盖其中的一些选项,
选择[:joiner]| |”和“
更好。或者,在函数的第一行中,您可以执行
opts=opts++[joiner:”和“]
,但这只有在所有选项都有默认值且存在两个默认值时才有意义。
def func_1(list, opts \\ [joiner: "AND"]) do
  helper(list, opts[:joiner])
  ...
end
defp helper(list, joiner) do
  ...
end