我正在学习Erlang并尝试将问题提到here,作为
3. Write a function mathStuff:perimeter(Form) which computes the perimeter of different forms. Form can be one of:
{square,Side}
{circle,Radius}
{triangle,A,B,C}
所以,我对此的解决方案是
-module(mathStuff).
-export([perimeter/1]).
perimeter(Form) ->
case element(1, Form) of
square -> io:format("perimeter for square is ~p~n", [4*element(2, Form)]);
circle -> io:format("perimeter for circle is ~p~n", [2*math:pi()*element(2, Form)]);
triangle -> io:format("perimeter for triangle ~p~n", [element(2, Form) + element(3, Form) + element(4, Form)])
end.
~
当我跑步时,我得到了
38> c("mathStuff.erl").
{ok,mathStuff}
39> mathStuff:perimeter({circle, 2}).
perimeter for circle is 12.566370614359172
ok
40> mathStuff:perimeter({rectangle, 2}).
** exception error: no case clause matching rectangle
in function mathStuff:perimeter/1 (mathStuff.erl, line 5)
41> mathStuff:perimeter({square, 2}).
perimeter for square is 8
ok
42> mathStuff:perimeter({traingle, 1, 2, 3}).
** exception error: no case clause matching traingle
in function mathStuff:perimeter/1 (mathStuff.erl, line 5)
43> mathStuff:perimeter({triangle, 1, 2, 3}).
perimeter for triangle 6
ok
但这是不正确的,因为我也可以
44> mathStuff:perimeter({triangle, 1, 2, 3, 4}).
perimeter for triangle 6
ok
45>
什么是解决此问题的更好方法,我如何更具体地了解类型和参数?
答案 0 :(得分:2)
第一个错误的原因是您在rectangle
表达式中未匹配case ... end
,因此无法匹配任何内容。与大多数命令式语言不同,函数式语言中的case
表达式通常需要匹配至少一个子句 - 否则,表达式应该评估什么值?
解决此问题的更惯用的方法是在函数头处使用pattern matching,如下所示:
perimeter({square, Side}) ->
io:format("perimeter for square is ~p~n", [4*Side]);
...
perimeter({triangle, A, B, C}) ->
io:format("perimeter for triangle ~p~n", [A + B + C]).
请注意,函数子句使用;
分隔,最后一个子句以.
终止。
本质上,Erlang不是手动选择带有element/2
的元组中的字段,而是允许您使用模式匹配来“解包”元组。在我看来,这是使用具有代数数据类型的函数式语言的最大好处之一。
顺便提一下,即使是perimeter({triangle, 1, 2, 3, 4})
这样的错误调用,您获得正确结果的原因是您没有使用模式匹配,而是手动选择了相关字段。元组。你从来没有检查过元组是不是太长了!模式匹配执行一种输入验证作为副作用,因为表达式如下:
{A, B, C} = {1, 2, 3, 4}
保证因匹配错误而失败。