轨道
/
Elixir
Elixir
/
练习
/
栈下溢
栈下溢

栈下溢

学习练习

简介

异常

Elixir 中所有的错误都实现了_异常行为_。就像_访问行为_一样,_异常行为_定义了一些回调函数,模块必须实现这些回调函数,才能满足该行为的软件契约。定义好一个错误之后,它就具有以下特性:

  • 模块的名字决定了错误的名字。
  • 模块定义了一个错误结构体。
  • 该结构体带有:message字段。
  • 模块可以配合raise/1和raise/2使用,抛出预期的错误。

_异常行为_还规定了两个回调函数:message/1和exception/1。如果没有实现它们,就会使用默认实现。用raise调用message/1时,它会把错误结构体转换成一条可读的消息。用raise/2调用exception/1时,可以为消息添加额外的上下文。

定义一个异常

要用错误模块定义一个异常,我们使用defexception宏:

# Defines a minimal error, with the name `MyError`
defmodule MyError do
  defexception message: "error"
end

# Defines an error with a customized exception/1 function
defmodule MyCustomizedError do
  defexception message: "custom error"

  @impl true
  def exception(value) do
    case value do
      [] ->
        %MyCustomizedError{}

      _ ->
        %MyCustomizedError{message: "Alert: " <> value}
    end
  end
end

使用异常

定义好的错误可以像内置错误一样使用,用raise/1或raise/2抛出。

  • raise/1通过模块名抛出特定的错误;如果参数是字符串,它会抛出一个RuntimeError,并以该字符串作为消息。
  • raise/2通过模块名抛出特定的错误,并接受一个属性实参,用来取得带有合适消息的错误。

说明

你继续在 Instruments of Texas 工作期间,RPN 计算器的 Elixir 实现迎来了新的进展。你的团队希望能抛出比标准库提供的通用错误更具体的错误。你正在做一些研究,但已经决定实现两个新的错误,它们都实现 Exception Behaviour。

1. 除以零的错误

用一个数除以零会产生未定义的结果,团队决定最好用一个错误来表示它。

实现DivisionByZeroError模块,让它的错误消息为:"division by zero occurred"

raise DivisionByZeroError
# => ** (DivisionByZeroError) division by zero occurred

2. 遇到栈下溢时的错误

RPN 计算器使用一个_栈_来记录尚未相加的数字。团队用一个数字数组(整数和浮点数)来表示这个_栈_,例如:[3, 4.0]。每个运算都需要栈上有特定数量的数字,才能完成计算。当栈上的数字不够时,就称为栈下溢错误。实现StackUnderflowError异常,它提供一个默认消息和可选的额外上下文

raise StackUnderflowError
# => ** (StackUnderflowError) stack underflow occurred

raise StackUnderflowError, "when dividing"
# => ** (StackUnderflowError) stack underflow occurred, context: when dividing

3. 编写一个做除法的函数

实现divide/1函数,它接收一个栈_(两个数字的数组)_,并且:

  • 当栈上的数字不够时抛出_栈下溢_
  • 当除数为 0 时抛出_除以零_(注意数字栈是按相反顺序存储的)
  • 没有错误抛出时执行除法
RPNCalculator.Exception.divide([])
# => ** (StackUnderflowError) stack underflow occurred, context: when dividing

RPNCalculator.Exception.divide([0, 100])
# => ** (DivisionByZeroError) division by zero occurred

RPNCalculator.Exception.divide([4, 16])
# => 4

注意,数组的顺序是相反的!

通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Elixir Exercism

准备好开始 栈下溢 了吗?

注册 Exercism,借助 58 个概念168 个练习 和真人导师指导,学习并掌握 Elixir,全部免费。