gpt4 book ai didi

elixir - 在 Elixir 中,为什么不使用 case 语句而不是多个函数重载?

转载 作者:行者123 更新时间:2023-12-04 09:58:36 25 4
gpt4 key购买 nike

我正在学习 Elixir 并且有点困惑为什么我们必须使用同一函数的多个定义而不是使用 case 语句进行分支。这是来自 Elixir in Action 的示例,第一版第 81 页,用于计算文件中的行数:

defmodule LinesCounter do
def count(path) do
File.read(path)
|> lines_num
end

defp lines_num({:ok, contents}) do
contents
|> String.split("\n")
|> length
end

defp lines_num({:error, _}), do: "error"
end

所以我们有两个 defp lines_num 实例来处理 :ok 和 :error 的情况。但是下面的内容难道不是以更简洁、更简洁的方式做同样的事情,并且只使用一个函数而不是三个函数吗?
defmodule LinesCounterCase do
def count(file) do
case File.read(file) do
{:ok, contents} -> contents |> String.split("\n") |> length
{:error, _} -> "error"
end
end
end

两者的工作方式相同。

当我开始使用 Elixir 时,我不想学习不正确的习语,因此我正在寻找以这种方式使用 case 语句的缺点。

最佳答案

书中的代码不是很惯用,它试图在不是最好的示例中显示多个函数子句和管道。

第 1 部分:关注点分离。

首先,一般约定说管道应该以“原始”变量开头,如下所示:

def count(path) do
path
|> File.read
|> lines_num
end

第二件事是这段代码确实混合了职责。有时,处理函数返回的类型也有好处。如果我看到,那 lines_num返回整数或字符串,我真的会挠头。为什么 lines_num读取文件时应该关心错误吗?答案是:不应该。它应该接受一个字符串并返回它的计算结果:
defp lines_num(contents) do #skipping the tuple here
contents
|> String.split("\n")
|> length
end

现在您的计数功能中有两个选项。您可以在文件出现问题时让它崩溃或处理错误。在这个例子中,只返回了字符串“error”,所以最惯用的方法是完全跳过它:
def count(path) do
path
|> File.read! #note the "!" it means it will return just content instead {:ok, content} or rise an error
|> lines_num
end
end

Elixir 几乎总是提供 func!版本,正是出于这个原因 - 使管道更容易。

如果要处理错误,case 语句是最好的。 Unix 管道也不鼓励分支。
def count(path) do
case File.read(path) do
{:ok, contents} -> lines_num(contents)
{:error, reason} -> do_something_on_error(reason)
end
end

第 2 部分:多重功能子句在何处有意义?

多函数子句优于 case 语句的主要情况有两种:递归和多态。还有一些其他的,但这些对于初学者来说应该足够了。

多态性

假设您要制作 lines_num更通用地处理字符表示列表:
defp lines_num(contents) when is_binary(contents) do
...
end
defp lines_num(contents) when is_list(contents) do
contents
|> :binary.list_to_bin #not the most efficient way!
|> lines_num
end

实现可能不同,但最终结果是相同的:不同类型的行数: "foo \n bar"'foo \n bar' .

递归
def factorial(0), do: 0
def factorial(n), do: n * factorial(n-1)

def map([], _func), do: []
def map([head, tail], func), do: [func.(head), map(tail)]

(警告:示例不是尾递归的)对此类函数使用 case 的可读性/惯用性要差得多。

结论:
  • 除非您知道自己在做什么,否则不要将函数头用于分支逻辑。
  • 如果您有分支逻辑,最好拆分管道。
  • 将函数子句用于多态性和递归。
  • 关于elixir - 在 Elixir 中,为什么不使用 case 语句而不是多个函数重载?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36119560/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com