代码之家  ›  专栏  ›  技术社区  ›  xji Paul Rada

如何重构嵌套过多的case语句

  •  0
  • xji Paul Rada  · 技术社区  · 7 年前

    在许多情况下,我发现自己需要对结构本身及其字段进行模式匹配,并在某些中间结果不为空时继续执行一些进一步的操作。

    然而,结构本身可能是 nil 首先。这导致我编写了多个嵌套匹配,例如

    experiment = Repo.get(Experiment, experiment_id)
    
    case experiment do
      nil ->
        # Error 1
    
      _ ->
        case experiment.active do
          false -> # Error 2
          true -> 
            case Repo.all(assoc(experiment, :experiment_results)) do
              [] -> # Error 3
              results -> # Do stuffs
        end
    end
    

    理想情况下,我希望编写代码时不要嵌套太多。

    如何重构代码?


    (注意,我最初的问题是关于结构上的模式匹配 . 我的实际用例比我最初提出的问题更广泛,因此我更新了问题。)

    Alekseimatiushkin和Sheharyar的答案适用的原始代码:

    experiment = Repo.get(Experiment, experiment_id)
    
    case experiment do
      nil ->
        :error
    
      _ ->
        case experiment.active do
          false -> :error
          true -> # Do stuffs
        end
    end
    
    3 回复  |  直到 7 年前
        1
  •  5
  •   Aleksei Matiushkin    7 年前

    我会选择直接模式匹配。

    Experiment
    |> Repo.get(experiment_id)
    |> case do
      %Experiment{active: true, other_attribute: :value} ->
        # do stuff
      _ ->
        :error
    end
    
        2
  •  1
  •   Sheharyar    7 年前

    那呢? && 短路操作员?

    if experiment && experiment.active && experiment.other do
      # do something
    else
      :error
    end
    

    您也可以使用 cond 要添加更多案例:

    cond do
      experiment && experiment.active && experiment.other ->
        # do something
    
      !experiment.active ->
        {:error, :inactive}
    
      is_nil(experiment) ->
        {:error, :experiment_is_nil}
    
      true ->
        {:error, :unknown}
    end
    
        3
  •  0
  •   xji Paul Rada    7 年前

    另一种方法是使用 with 语法。它基本上规定了“快乐的道路”应该是什么,即使道路需要多个相对复杂的检查:

    with experiment <- Repo.get(Experiment, id),
         {:nil_experiment, false} <- {:nil_experiment, is_nil(experiment)},
         experiment_results <- Repo.all(assoc(experiment, :experiment_results)),
         {:empty_results, false} <- {:empty_results, Enum.empty?(experiment_results)} do
    do
      # Do stuffs with `experiment_results`
    else
      {:nil_experiment, true} ->
         # Error message 1
    
      {:empty_results, true} ->
         # Error message 2
    
      _ ->
         # Unknown error
    end