假设我们有以下实现:
defmodule SomeModule do
@celcius_coefficient 800
@rheinan_coefficient 900
@fahrenheit_coefficient 1000
@available_units [:celcius, :rheinan, :fahrenheit]
def foo(some_struct) do
some_var = get_var(some_struct, :var_name)
if some_var == :kelvin do
initialize(some_struct)
else
bar(some_struct, some_var)
end
end
defp bar(struct, :celcius) do
mult(struct, @celcius_coefficient)
end
defp bar(struct, :rheinan) do
mult(struct, @rheinan_coefficient)
end
defp bar(struct, :fahrenheit) do
mult(struct, @fahrenheit_coefficient)
end
end
有一些以*_coefficient
开头的模块属性,其中*
是一个表示内容的关键字。我们正在实施bar/2
以匹配所有可用子句。实现很麻烦,当我们需要添加一个新单元时,我们需要实现一个新函数来匹配新单元,这个单元将由同一个表达式进行评估。
An implementation in Comeonin library使用理解来为所有子句生成函数。我建议采用以下方法:
defmodule SomeModule do
@celcius_coefficient 800
@rheinan_coefficient 900
@fahrenheit_coefficient 1000
@available_units [:celcius, :rheinan, :fahrenheit]
def foo(some_struct) do
some_var = get_var(some_struct, :var_name)
if some_var == :kelvin do
initialize(some_struct)
else
bar(some_struct, some_var)
end
end
for unit <- @available_units do
defp bar(struct, unquote(unit)) do
coefficient = unquote(Module.get_attribute(__MODULE__, :"#{Atom.to_string(unquote(unit))}_coefficient"))
mult(struct, coefficient)
end
end
end
在编译期间应使用Module.get_attribute/2
检索模块属性,因为它在运行时不可用。
我们还需要取消引用unit
参数,以便为函数提供正确的原子。
它抛出一个编译错误,说:unquote called outside quote
。
试图在某些地方放置quote
条款,没有任何帮助。
我想我对Elixir中的元编程有一些误解和混淆。
你有什么建议吗?
我还处于使用defp
或defmacrop
来定义bar/2
的两难境地。做这种事情的正确结构是什么?
谢谢。
答案 0 :(得分:1)
您不需要在内部unit
值周围取消引用,因为它已经没有引用:
coefficient = unquote(
Module.get_attribute(__MODULE__, :"#{Atom.to_string(unit)}_coefficient")
)
程序:
defmodule SomeModule do
@celcius_coefficient 800
@rheinan_coefficient 900
@fahrenheit_coefficient 1000
@available_units [:celcius, :rheinan, :fahrenheit]
for unit <- @available_units do
def bar(struct, unquote(unit)) do
coefficient = unquote(Module.get_attribute(__MODULE__, :"#{Atom.to_string(unit)}_coefficient"))
{struct, coefficient}
end
end
end
IO.inspect SomeModule.bar(1, :celcius)
IO.inspect SomeModule.bar(2, :fahrenheit)
输出:
{1, 800}
{2, 1000}
(我将该功能公开用于测试,如果您不想在此模块之外使用它,可以再次将其设为私有)
我使用defp或defmacrop定义bar / 2也处于两难境地。做这种事情的正确结构是什么?
仅在需要将此功能用作宏时才定义宏。在这种情况下,您不需要,因此defp
(或def
)没问题。