ESC
输入关键词搜索文章
目录

标准库

Build Your Own Lisp · 第 15 章
为 Lisp 实现标准库
Chapter 15 · 标准库
内置函数

章节信息

原书章节:第 15 章 Standard Library

中文翻译KSCO (GitHub)

原书地址buildyourownlisp.com

标准库概述

在本章中,我们将为我们的 Lisp 实现一个标准库。标准库是一组预定义的函数,它们提供了常用的功能,让用户可以更方便地编写程序。

我们将把标准库写成 Lisp 代码,然后在启动时加载它。这样做的好处是:

  • 标准库可以用 Lisp 本身来编写,不需要修改 C 代码
  • 用户可以查看和学习标准库的实现
  • 标准库可以被扩展和修改
数学函数

我们将实现以下数学函数:

数学内置函数

  • +:加法
  • -:减法
  • *:乘法
  • /:除法
  • %:取模
  • ^:幂运算
  • min:最小值
  • max:最大值

这些函数已经在前面的章节中实现了。现在我们需要确保它们都被正确注册到环境中。

列表操作

列表是 Lisp 中最重要的数据结构之一。我们将实现以下列表操作:

列表内置函数

  • list:创建列表
  • head:获取列表第一个元素
  • tail:获取列表除第一个元素外的部分
  • join:连接列表
  • eval:求值列表
  • cons:将元素添加到列表头部
  • len:获取列表长度
  • init:获取列表除最后一个元素外的部分
  • last:获取列表最后一个元素
lval* builtin_list(lenv* e, lval* a) {
  a->type = LVAL_QEXPR;
  return a;
}

lval* builtin_head(lenv* e, lval* a) {
  LASSERT_NUM("head", a, 1);
  LASSERT_TYPE("head", a, 0, LVAL_QEXPR);
  LASSERT_NOT_EMPTY("head", a, 0);

  lval* v = lval_take(a, 0);
  while (v->count > 1) { lval_del(lval_pop(v, 1)); }
  return v;
}

lval* builtin_tail(lenv* e, lval* a) {
  LASSERT_NUM("tail", a, 1);
  LASSERT_TYPE("tail", a, 0, LVAL_QEXPR);
  LASSERT_NOT_EMPTY("tail", a, 0);

  lval* v = lval_take(a, 0);
  lval_del(lval_pop(v, 0));
  return v;
}
控制流

控制流函数用于控制程序的执行顺序:

控制流内置函数

  • if:条件分支
  • while:循环
  • do:顺序执行多个表达式
lval* builtin_while(lenv* e, lval* a) {
  LASSERT_NUM("while", a, 2);
  LASSERT_TYPE("while", a, 0, LVAL_QEXPR);
  LASSERT_TYPE("while", a, 1, LVAL_QEXPR);

  lval* cond = lval_pop(a, 0);
  lval* body = lval_pop(a, 0);
  lval_del(a);

  while (1) {
    lval* c = lval_eval(e, lval_copy(cond));

    if (c->num == 0) {
      lval_del(c);
      break;
    }
    lval_del(c);

    lval* x = lval_eval(e, lval_copy(body));
    if (x->type == LVAL_ERR) {
      lval_del(cond); lval_del(body);
      return x;
    }
    lval_del(x);
  }

  lval_del(cond); lval_del(body);
  return lval_sexpr();
}

复习速查

  • 标准库:用 Lisp 编写的预定义函数集合
  • 数学函数+-*/%^
  • 列表操作listheadtailjoincons
  • 控制流ifwhiledo