繁体   English   中英

是否可以重新创建erlang:数学函数作为elixir宏?

[英]Is it possible to recreate erlang's :math functions as elixir macros?

我正在创建一个宏来计算两组lat-long值之间的距离。

iex()> calc_distance(posA, posB)
2  # distance is in km

目前,这与常规功能类似。 我希望它成为一个宏的原因是我可以在一个保护条款中使用它。 例如

fn(posA, posB) when calc_distance(posA, posB) < 10 -> "close enough" end

但是,对于要在保护条款中使用的宏,它必须“ 遵循规则 ”。 这意味着不允许使用许多功能和操作符。

我最初的宏看起来像......

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函数。

如果我可以将我自己的这个函数版本编写为宏,这将解决问题。

有谁知道这是否可能? 如果是这样,我怎么能这样做呢?

不,不可能将其作为防护测试来实现。

或者,如果您允许丢失准确性,则可以实现: 正弦函数的这种近似可以仅使用保护中允许的操作来实现。

但最有可能的是,在您的程序中,准确性比保存几行代码更高。 在这种情况下,我可能会调用我的函数call_distance并将结果作为参数传递给另一个函数,该函数可以对结果使用保护测试:

def my_function(ll1, ll2) do
    my_function(ll1, ll2, calc_distance(ll1, ll2))
end

defp my_function(ll1, ll2, distance) when distance < 10 do
    "close enough"
end
defp my_function(ll1, ll2, distance) do
    "too far"
end

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM