如何重构两个相似的函数以减少 Elixir 中的代码重复?
How can I refactor two similar functions to reduce the code duplication in Elixir?
我有以下两个功能:
defp select_previous_scheduled_price(scheduled_prices, date) do
if length(scheduled_prices) > 1 do
before_prices = Enum.filter(scheduled_prices, &starts_before(&1, date))
if !Enum.empty?(before_prices) do
hd(before_prices)
else
nil
end
else
nil
end
end
defp select_next_scheduled_price(scheduled_prices, date) do
if length(scheduled_prices) >= 1 do
after_prices = Enum.filter(scheduled_prices, &starts_after(&1, date))
if !Enum.empty?(after_prices) do
hd(after_prices)
else
nil
end
else
nil
end
end
有两个区别:
1. 第二行的运算符(即 >
与 >=
);和
2. 第三行过滤调用的函数3(即 &starts_before/2
vs &starts_after/2
)
由于区别在于运算符而不是必须应用局部值和参数化值的函数和函数,我不完全清楚是否或如何将其分解。
换句话说,我想做这样的解决方案(只是实际起作用,而这不会):
defp select_previous_scheduled_price(scheduled_prices, date) do
select_scheduled_price(scheduled_prices, date, >, &starts_before/2)
end
defp select_next_scheduled_price(scheduled_prices, date) do
select_scheduled_price(scheduled_prices, date, >=, &starts_after/2)
end
defp select_scheduled_price(scheduled_prices, date, meets_length_criteria, filter_criteria) do
if meets_length_criteria(scheduled_prices, 1) do
qualified_prices = Enum.filter(scheduled_prices, &filter_criteria(&1, date))
if !Enum.empty?(qualified_prices) do
hd(qualified_prices)
else
nil
end
else
nil
end
end
关于如何使这项工作有任何想法吗?
谢谢!
List.first/1
instead of hd/1
首先会消除嵌套 if
的必要性。然后我将代码拆分成更小的函数来阐明意图。
defp if_prices(:before, prices, date),
do: {length(prices) > 1, &starts_before(&1, date)}
defp if_prices(:after, prices, date),
do: {length(prices) >= 1, &starts_after(&1, date)}
defp select_previous_scheduled_price(scheduled_prices, date),
do: select_scheduled_price(:before, scheduled_prices, date)
defp select_next_scheduled_price(scheduled_prices, date),
do: select_scheduled_price(:after, scheduled_prices, date)
defp select_scheduled_price(direction, prices, date) do
case if_prices(direction, prices, date) do
{false, _} -> nil
{_, fun} ->
prices
|> Enum.filter(fun)
|> List.first()
end
end
这是一个与您尝试的版本非常相似的版本。它编译(但我没有运行它与真实数据)。
defp select_previous_scheduled_price(scheduled_prices, date) do
select_scheduled_price(scheduled_prices, date, &>/2, &starts_before/2)
end
defp select_next_scheduled_price(scheduled_prices, date) do
select_scheduled_price(scheduled_prices, date, &>=/2, &starts_after/2)
end
defp select_scheduled_price(scheduled_prices, date, meets_length_criteria, filter_criteria) do
if meets_length_criteria.(scheduled_prices, 1) do
scheduled_prices
|> Enum.filter(&filter_criteria.(&1, date))
|> List.first()
else
nil
end
end
显着变化:
- 要传递二元运算符,您可以使用
&
和 /2
:>=
变为 &>=/2
。
- 要调用这些函数,您需要使用
.
。
- 我使
if
逻辑更加地道。
我有以下两个功能:
defp select_previous_scheduled_price(scheduled_prices, date) do
if length(scheduled_prices) > 1 do
before_prices = Enum.filter(scheduled_prices, &starts_before(&1, date))
if !Enum.empty?(before_prices) do
hd(before_prices)
else
nil
end
else
nil
end
end
defp select_next_scheduled_price(scheduled_prices, date) do
if length(scheduled_prices) >= 1 do
after_prices = Enum.filter(scheduled_prices, &starts_after(&1, date))
if !Enum.empty?(after_prices) do
hd(after_prices)
else
nil
end
else
nil
end
end
有两个区别:
1. 第二行的运算符(即 >
与 >=
);和
2. 第三行过滤调用的函数3(即 &starts_before/2
vs &starts_after/2
)
由于区别在于运算符而不是必须应用局部值和参数化值的函数和函数,我不完全清楚是否或如何将其分解。
换句话说,我想做这样的解决方案(只是实际起作用,而这不会):
defp select_previous_scheduled_price(scheduled_prices, date) do
select_scheduled_price(scheduled_prices, date, >, &starts_before/2)
end
defp select_next_scheduled_price(scheduled_prices, date) do
select_scheduled_price(scheduled_prices, date, >=, &starts_after/2)
end
defp select_scheduled_price(scheduled_prices, date, meets_length_criteria, filter_criteria) do
if meets_length_criteria(scheduled_prices, 1) do
qualified_prices = Enum.filter(scheduled_prices, &filter_criteria(&1, date))
if !Enum.empty?(qualified_prices) do
hd(qualified_prices)
else
nil
end
else
nil
end
end
关于如何使这项工作有任何想法吗?
谢谢!
List.first/1
instead of hd/1
首先会消除嵌套 if
的必要性。然后我将代码拆分成更小的函数来阐明意图。
defp if_prices(:before, prices, date),
do: {length(prices) > 1, &starts_before(&1, date)}
defp if_prices(:after, prices, date),
do: {length(prices) >= 1, &starts_after(&1, date)}
defp select_previous_scheduled_price(scheduled_prices, date),
do: select_scheduled_price(:before, scheduled_prices, date)
defp select_next_scheduled_price(scheduled_prices, date),
do: select_scheduled_price(:after, scheduled_prices, date)
defp select_scheduled_price(direction, prices, date) do
case if_prices(direction, prices, date) do
{false, _} -> nil
{_, fun} ->
prices
|> Enum.filter(fun)
|> List.first()
end
end
这是一个与您尝试的版本非常相似的版本。它编译(但我没有运行它与真实数据)。
defp select_previous_scheduled_price(scheduled_prices, date) do
select_scheduled_price(scheduled_prices, date, &>/2, &starts_before/2)
end
defp select_next_scheduled_price(scheduled_prices, date) do
select_scheduled_price(scheduled_prices, date, &>=/2, &starts_after/2)
end
defp select_scheduled_price(scheduled_prices, date, meets_length_criteria, filter_criteria) do
if meets_length_criteria.(scheduled_prices, 1) do
scheduled_prices
|> Enum.filter(&filter_criteria.(&1, date))
|> List.first()
else
nil
end
end
显着变化:
- 要传递二元运算符,您可以使用
&
和/2
:>=
变为&>=/2
。 - 要调用这些函数,您需要使用
.
。 - 我使
if
逻辑更加地道。