Étude 3-1: 模式匹配

area函数中使用原子模式匹配来计算一个矩形, 三角形椭圆形的面积. 如果你的参数为shape, a, b:

面积公式为:

TYPE FORMULA DESCRIPTION
:rectange a * b 矩形
:triangle a * b / 2.0 三角形
:ellipse :math.pi() * a * b 椭圆形

实现代码:

defmodule Geom do
  @moduledoc """ 
  Functions for calculating areas of geometric shapes. 
  from *Études for Elixir*, O'Reilly Media, Inc., 2013.
  Copyright 2013 by J. David Eisenberg.
  """ 
  @vsn 0.1 

  @doc """
  Calculates the area of a shape, given the shape and two of the
  dimensions. Returns the product of its arguments for a rectangle,
  one half the product of the arguments for a triangle, and
  :math.pi times the product of the arguments for an ellipse.
  """
  
  @spec area(atom(), number(), number()) :: number()
  
  def area(:rectangle, length, width) do
    length * width
  end
  
  def area(:triangle, base, height) do
    base * height / 2.0
  end
  
  def area(:ellipse, a, b) do
    :math.pi * a * b
  end
end

Étude 3-2: Guards

在函数匹配的情况下, 再加上一些限制的匹配条件. 多用于限制参数的类型, 值的取值范围

参考了一些资料, 多译为保护式, 多用于限制取值范围避免取到无效的值, 比如数学中除数为0是无意义的. 因此需要限制除数大于0

下面以area/3为例子来说明如何使用保护式

defmodule Geom do
  @spec area(atom(), number(), number()) :: number()
  
  def area(:rectangle, length, width) when length >= 0 and width >= 0 do
    length * width
  end
  
  def area(:triangle, base, height) when base >= 0 and height >= 0 do
    base * height / 2.0
  end
  
  def area(:ellipse, a, b) when a >= 0 and b >= 0 do
    :math.pi * a * b
  end
end

例如area(:rectangle, length, width)矩形面积函数分句, 除了传递三个必须的参数意外, 当length > 0并且width >= 0, 才会匹配到这个分句, 否则匹配不到任何area函数, 这时候Erlang VM会刨除无法匹配的错误, 如下:

iex(60)> Geom.area(:rectangle, -1, 10)
** (FunctionClauseError) no function clause matching in Geom.area/3
    iex:61: Geom.area(:rectangle, -1, 10)

要处理这类无法匹配的错误, 请看Étude 3-3.

Étude 3-3: 下划线

defmodule Geom do
  
  @spec area(atom(), number(), number()) :: number()
  
  def area(:rectangle, length, width) when length >= 0 and width >= 0 do
    length * width
  end
  
  def area(:triangle, base, height) when base >= 0 and height >= 0 do
    base * height / 2.0
  end
  def area(:ellipse, a, b) when a >= 0 and b >= 0 do
    :math.pi * a * b
  end
  
  def area(_, _, _) do
    0
  end
end

这里我们增加了第四个函数用于匹配其他非:rectangle,:triangle,:ellipse形状的默认调用返回0.

Étude 3-4: 把元组作为参数

defmodule Geom do
  
  @spec area({atom(), number(), number()}) :: number()
  
  # 把元组作为参数
  def area({shape, dim1, dim2}) do
    area(shape, dim1, dim2)
  end
  
  # Individual functions to handle each shape

  @spec area(atom(), number(), number()) :: number()
  
  defp area(:rectangle, length, width) when length >= 0 and width >= 0 do
    length * width
  end
  
  defp area(:triangle, base, height) when base >= 0 and height >= 0 do
    base * height / 2.0
  end
  
  defp area(:ellipse, a, b) when a >= 0 and b >= 0 do
    :math.pi * a * b
  end
  
  defp area(_, _, _) do
    0
  end
end

developerworks
1.7k 声望266 粉丝