保护整数元组

时间:2013-09-16 21:56:28

标签: erlang

我有一个函数接受坐标(元组)作为其参数之一:

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

我想确保坐标:

  • 是包含2个项目(X和Y)的元组
  • X和Y都是整数

我可以使用上面的防护,它可以正常工作。但是,我有许多使用坐标的函数,我想知道我是否可以以某种方式清理这个构造(一些宏?)所以会有类似的东西:

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

这样做是否有干净且惯用的方式?是erlang-ish吗?

修改

Erlang docs明确阻止防御性编程:

  

3.13不要“防御性地”编程

     

防御性程序是程序员不“信任”的程序   将数据输入到他们正在编程的系统部分。一般来说   一个人不应该测试输入数据到函数的正确性。大多数   系统中的代码应该假设为   输入有关功能的数据是正确的。只有一小部分   代码应该实际执行任何数据检查。这是   通常在数据第一次“进入”系统时完成一次   数据在进入系统后已经过检查   假设是正确的。

7 个答案:

答案 0 :(得分:7)

有一个干净的,我认为相当于Erlang-ish的方式来定义is_coord宏:

-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) ->
    ...

请注意tuple_size/1也暗示is_tuple/1检查。

答案 1 :(得分: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,或者使用模板引擎预处理您的源代码,例如小胡子:https://github.com/mojombo/mustache.erl

希望它有所帮助!

答案 2 :(得分:3)

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

1)定义一个类型

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

2)使用Erlang的dialyzer

答案 3 :(得分:3)

另一种方法是使用像guardian这样的解析变换库,并编写如下代码。

-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语句函数所写的类似函数。

答案 4 :(得分: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

答案 5 :(得分: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)的元组 [已选中]

答案 6 :(得分: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.