【发布时间】:2019-07-28 05:25:05
【问题描述】:
我正在创建一个宏来计算两组经纬度值之间的距离。
iex()> calc_distance(posA, posB)
2 # distance is in km
目前,它的工作方式类似于常规功能。我希望它成为宏的原因是我可以在保护子句中使用它。 例如
fn(posA, posB) when calc_distance(posA, posB) < 10 -> "close enough" end
但是,要在保护子句中使用宏,它必须是“follow the rules”。这意味着很多函数和运算符都不允许使用。
我最初的宏看起来是这样的......
defmacro calc_distance(ll1, ll2) do
quote do
lat1 = elem(unquote(ll1), 0)
long1 = elem(unquote(ll1), 1)
lat2 = elem(unquote(ll2), 0)
long2 = elem(unquote(ll2), 1)
v = :math.pi / 180
r = 6372.8
dlat = :math.sin((lat2 - lat1) * v / 2)
dlong = :math.sin((long2 - long1) * v / 2)
a = dlat * dlat + dlong * dlong * :math.cos(lat1 * v) * :math.cos(lat2 * v)
res = r * 2 * :math.asin(:math.sqrt(a))
res
end
end
我已经开始通过删除宏中定义的所有变量来使其“保护条款友好”。
defmacro calc_distance(ll1, ll2) do
quote do
:math.sin((elem(unquote(ll2), 1) - elem(unquote(ll1), 1)) * (3.141592653589793 / 180) / 2)
|> square()
|> Kernel.*(:math.cos(elem(unquote(ll1), 0) * (3.141592653589793 / 180)))
|> Kernel.*(:math.cos(elem(unquote(ll2), 0) * (3.141592653589793 / 180)))
|> Kernel.+(square(:math.sin((elem(unquote(ll2), 0) - elem(unquote(ll1), 0)) * (3.141592653589793 / 180) / 2)))
|> :math.sqrt()
|> :math.asin()
|> Kernel.*(2)
|> Kernel.*(6372.8)
end
end
这仍然可以用作宏,但是当我尝试将其用作保护子句时仍然会出错,因为正在使用 :math 函数。
如果我可以将此函数的自己版本编写为宏,这将解决问题。
有人知道这是否可能吗?如果是这样,我该怎么办?
【问题讨论】:
标签: erlang elixir metaprogramming