3

I have a function that accepts coordinates (tuple) as one of its arguments:

func({X, Y}, Something) when is_integer(X), is_integer(Y) -> ...

I want to ensure that the coordinates:

  • are tuple with 2 items (X and Y)
  • both X and Y are integers

I can use the guard as above, and it works all right. But, I have many functions that use the coordinates and I wanted to know if I can clean up somehow this construct (some macro?) so would have something like:

func(XY, Something) when ?is_coord(XY) -> ... % how to define ?is_coord

Is there a clean and idiomatic way to do that? Is it erlang-ish?

Edit

Erlang docs explicitly discourage defensive programming:

3.13 Do not program "defensively"

A defensive program is one where the programmer does not "trust" the input data to the part of the system they are programming. In general one should not test input data to functions for correctness. Most of the code in the system should be written with the assumption that the input data to the function in question is correct. Only a small part of the code should actually perform any checking of the data. This is usually done when data "enters" the system for the first time, once data has been checked as it enters the system it should thereafter be assumed correct.

4

7 回答 7

7

There's a clean and I think pretty Erlang-ish way to define is_coord macro:

-define(is_coord(C), tuple_size(C) =:= 2
                     andalso is_integer(element(1, C))
                     andalso is_integer(element(2, C))).

func(Coord, Something) when ?is_coord(Coord) ->
    ...

Note that tuple_size/1 also implies is_tuple/1 check.

于 2013-09-17T07:18:27.503 回答
3

Ning 和 marcelog 的答案既好又高效,但就我个人而言,我要么让代码保持原样,要么使用其中一个,然后:

1) 定义一个类型

-type point() :: {integer(),integer()}.

2)使用Erlang的透析器

于 2013-09-16T22:26:02.527 回答
3

嗯,你不能准确定义自己的警卫,因为我们的想法是完全确定它们没有副作用(http://www.erlang.org/doc/reference_manual/expressions.html#id80042)。

这个快速破解有效:

-define(GUARD(Name, Args), Name({X, Y}, Args) when is_integer(X), is_integer(Y)).
-export([myfun/2]).

?GUARD(myfun, [A, B, C]) ->
  io:format("hi~n"),
  ok.

虽然我个人不喜欢它......如果你真的需要它,也许你可以做一个解析转换:http: //chlorophil.blogspot.com.ar/2007/04/erlang-macro-processor-v1-part- i.html,或使用模板引擎对源代码进行预处理,例如 mustache:https ://github.com/mojombo/mustache.erl

希望能帮助到你!

于 2013-09-16T22:21:02.347 回答
3

另一种方法是使用像监护人这样的解析转换库并编写如下代码。

-compile({parse_transform, guardian}).

func(XY, Something) when is_coord(XY) ->
    do(Something);
func(XY, A) ->
    filering_out.

is_coord({X, Y}) when is_integer(X), is_integer(Y)->
   true;
is_coord(_) ->
   false.

func 函数被转换为与 Ning 的 case 语句函数编写的类似函数。

于 2013-09-17T03:04:25.927 回答
2

您可以使用case

-module(lab).

-compile(export_all).

go() ->
    func({1, 2}, "1st try"),
    func({a, 2}, "2nd try"),
    func({1, 2, 3}, "3rd try").

func(XY, Something) ->
    case is_coord(XY) of
        true -> io:format("~p~n", [Something]);
        false -> io:format("Not a coord~n")
    end.

is_coord(XY) ->
    case XY of
        {X, Y} when is_integer(X), is_integer(Y) ->
            true;
        _ ->
            false
    end.

测试运行:

> c(lab), lab:go().
"1st try"
Not a coord
Not a coord
ok
于 2013-09-16T22:16:52.350 回答
1

我会说记录检查 (is_record) 不能保证两个元素都是整数。所以,如果你需要确保你有 2 个带有整数的元素元组,我会使用

 -define(is_coord(X), size(X)== 2 andalso is_integer(element(1,X)) andalso is_integer(element(2,X))).

 rr(X) when ?is_coord(X) -> coord;
 rr(_) -> not_coord.
于 2013-09-17T06:46:48.063 回答
1

我刚刚意识到我们可以有另一个解决方案。

我们可以将coord记录定义为:

-define(coord, {x = 0, y = 0}).

然后我们可以简单地做:

func(XY, Something) when is_record(XY, coord) -> ...

我们需要确保在创建记录x时使用整数进行初始化。(应该不难:))ycoord

... X 和 Y 都是整数[已选中]

is_record(XY, coord)保证 的结构XY

...是包含 2 个项目(X 和 Y)的元组[已选中]

于 2013-09-16T22:51:43.107 回答