Erlang if 与 case

上面的 find_max_and_min 函数可以找到温度的最大值与最小值。这儿介绍一个新的结构 if。If 的语法格式如下:

  1. if
  2. Condition 1 ->
  3. Action 1;
  4. Condition 2 ->
  5. Action 2;
  6. Condition 3 ->
  7. Action 3;
  8. Condition 4 ->
  9. Action 4
  10. end

注意,在 end 之前没有 “;”。条件(Condidtion)的工作方式与 guard 一样,即测试并返回成功或者失败。Erlang 从第一个条件开始测试一直到找到一个测试为真的分支。随后,执行该条件后的动作,且忽略其它在 end 前的条件与动作。如果所有条件都测试失败,则会产生运行时错误。一个测试恒为真的条件就是 true。它常用作 if 的最后一个条件,即当所有条件都测试失败时,则执行 true 后面的动作。

下面这个例子说明了 if 的工作方式:

  1. -module(tut9).
  2. -export([test_if/2]).
  3. test_if(A, B) ->
  4. if
  5. A == 5 ->
  6. io:format("A == 5~n", []),
  7. a_equals_5;
  8. B == 6 ->
  9. io:format("B == 6~n", []),
  10. b_equals_6;
  11. A == 2, B == 3 -> %That is A equals 2 and B equals 3
  12. io:format("A == 2, B == 3~n", []),
  13. a_equals_2_b_equals_3;
  14. A == 1 ; B == 7 -> %That is A equals 1 or B equals 7
  15. io:format("A == 1 ; B == 7~n", []),
  16. a_equals_1_or_b_equals_7
  17. end.

测试该程序:

  1. 60> c(tut9).
  2. {ok,tut9}
  3. 61> tut9:test_if(5,33).
  4. A == 5
  5. a_equals_5
  6. 62> tut9:test_if(33,6).
  7. B == 6
  8. b_equals_6
  9. 63> tut9:test_if(2, 3).
  10. A == 2, B == 3
  11. a_equals_2_b_equals_3
  12. 64> tut9:test_if(1, 33).
  13. A == 1 ; B == 7
  14. a_equals_1_or_b_equals_7
  15. 65> tut9:test_if(33, 7).
  16. A == 1 ; B == 7
  17. a_equals_1_or_b_equals_7
  18. 66> tut9:test_if(33, 33).
  19. ** exception error: no true branch found when evaluating an if expression
  20. in function tut9:test_if/2 (tut9.erl, line 5)

注意,tut9:test_if(33,33) 使得所有测试条件都失败,这将导致产生一个 if_clause 运行时错误。参考 Guard 序列 可以得到更多关于 guard 测试的内容。

Erlang 中还有一种 case 结构。回想一下前面的 convert_length 函数:

  1. convert_length({centimeter, X}) ->
  2. {inch, X / 2.54};
  3. convert_length({inch, Y}) ->
  4. {centimeter, Y * 2.54}.

该函数也可以用 case 实现,如下所示:

  1. -module(tut10).
  2. -export([convert_length/1]).
  3. convert_length(Length) ->
  4. case Length of
  5. {centimeter, X} ->
  6. {inch, X / 2.54};
  7. {inch, Y} ->
  8. {centimeter, Y * 2.54}
  9. end.

无论是 case 还是 if 都有返回值。这也就是说,上面的例子中,case 语句要么返回 {inch,X/2.54} 要么返回 {centimeter,Y*2.54}。case 语句也可以用 guard 子句来实现。下面的例子可以帮助你分清二者。这个例子中,输入年份得到指定某月的天数。年份必须是已知的,因为闰年的二月有 29 天,所以必须根据年份才能判断二月的天数。

  1. -module(tut11).
  2. -export([month_length/2]).
  3. month_length(Year, Month) ->
  4. %% 400 整除的为闰年。
  5. %% 100 整除但不能被 400 整除的不是闰年。
  6. %% 4 整除但不能被 100 整除的为闰年。
  7. Leap = if
  8. trunc(Year / 400) * 400 == Year ->
  9. leap;
  10. trunc(Year / 100) * 100 == Year ->
  11. not_leap;
  12. trunc(Year / 4) * 4 == Year ->
  13. leap;
  14. true ->
  15. not_leap
  16. end,
  17. case Month of
  18. sep -> 30;
  19. apr -> 30;
  20. jun -> 30;
  21. nov -> 30;
  22. feb when Leap == leap -> 29;
  23. feb -> 28;
  24. jan -> 31;
  25. mar -> 31;
  26. may -> 31;
  27. jul -> 31;
  28. aug -> 31;
  29. oct -> 31;
  30. dec -> 31
  31. end
  1. 70> c(tut11).
  2. {ok,tut11}
  3. 71> tut11:month_length(2004, feb).
  4. 29
  5. 72> tut11:month_length(2003, feb).
  6. 28
  7. 73> tut11:month_length(1947, aug).
  8. 31