簡體   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