标签: Lisp

  • CLOG:Common Lisp 万能 GUI – 踏入 Web GUI 世界之旅

    CLOG:Common Lisp 万能 GUI – 踏入 Web GUI 世界之旅

    🚀 引言:CLOG – Lisp 驱动的 Web GUI 框架

    想象一个世界,你可以用 Common Lisp 的优雅和表达能力构建强大的跨平台图形用户界面 (GUI)。这就是 CLOG,Common Lisp 万能 GUI,所带来的承诺。CLOG 利用 Web 技术的力量,创建交互式、动态的 GUI,模糊了传统 GUI 框架和 Web 开发之间的界限。

    CLOG 不仅仅是一个框架;它是一个通往思考 GUI 开发新方式的桥梁。它允许你构建可以在本地或远程运行的应用程序,无缝地适应不同的平台和设备。把它想象成连接 Lisp 世界与庞大的 Web 技术生态系统的桥梁。

    📚 入门:踏入 CLOG 宇宙的第一步

    CLOG 的设计既适合经验丰富的 Lisp 程序员,也适合新手。要开始,你需要安装 CLOG 和 Quicklisp(Common Lisp 的包管理器)。

    💡 CLOG REPL:你的交互式游乐场

    安装完 CLOG 后,你可以沉浸在 CLOG REPL 的交互式世界中。这个 REPL 充当你的指挥中心,允许你用 CLOG 命令进行实验,并在浏览器窗口中看到结果。

    💻 构建你的第一个 CLOG 应用程序

    让我们从一个简单的 “Hello World!” 示例开始:

    (ql:quickload :clog)
    (clog:clog-repl)
    (in-package clog-user)
    (create-div *body* :content "Hello World!")

    这段代码做了以下事情:

    1. 加载 CLOG 包。
    2. 启动 CLOG REPL,打开一个浏览器窗口。
    3. 进入 clog-user 包,提供对 CLOG 函数的访问。
    4. 在浏览器窗口的 *body* 中创建一个 div 元素,显示 “Hello World!”

    🚀 CLOG 事件:与你的 GUI 交互

    CLOG 提供了一个全面的事件系统,允许你响应用户交互,例如点击、鼠标移动和键盘输入。

    例如,以下是如何创建一个按钮,当点击时改变其背景颜色:

    (let ((tmp (create-button *body* :content "Click Me")))
      (set-on-click tmp
        (lambda (obj)
          (setf (background-color tmp) :red))))

    这段代码:

    1. 创建一个按钮。
    2. 定义一个函数,将按钮的背景颜色更改为红色。
    3. 将此函数作为按钮的 on-click 处理程序附加。

    🌐 CLOG 和 Web 技术:强大的合作关系

    CLOG 与 HTML、CSS 和 JavaScript 等 Web 技术无缝集成。你可以使用这些熟悉的工具来设计和样式化你的 CLOG 应用程序,创建视觉上吸引人和交互式的界面。

    💡 CLOG 对象:构建 GUI 的积木

    CLOG 提供了一组丰富的对象,代表各种 GUI 元素,例如按钮、文本字段、图像等等。这些对象经过精心设计,映射到它们的 HTML 对应物,允许你创建复杂而精致的用户界面。

    🚀 CLOG:未来框架

    CLOG 是一个强大的框架,为 GUI 开发打开了无限的可能性。它利用 Web 技术的能力、直观的 Lisp 语法和对交互性的关注,使其成为构建现代跨平台应用程序的引人注目的选择。

    📚 参考资料

    🎉 加入 CLOG 革命

    CLOG 是一个充满活力且不断发展的社区。加入讨论版,探索用 Lisp 进行 Web GUI 开发的世界。GUI 开发的未来就在这里,它由 CLOG 提供支持!

  • Hunchentoot:你的 Common Lisp 网页服务器探险之旅 🗺️

    Hunchentoot 就像一把瑞士军刀,专为 Common Lisp 网页开发者打造。它既是网页服务器,也是构建动态网站的工具箱。想象一下,它就像森林里的一间舒适小屋,随时准备接待你的网页应用,并提供所有你需要构建熊熊烈火和美味数字内容的工具。 🏕️🔥

    下载和安装:快速去商店一趟 🛒

    在你开始构建你的网页杰作之前,你需要收集一些物资。Hunchentoot 依赖于几个其他 Common Lisp 库,比如可靠的 MD5 用于安全,CL-BASE64 用于编码,以及 RFC2388 用于处理网页协议。就像为你的网页服务器准备了一个储备充足的食品储藏室。

    别担心,你可以很容易地从互联网上获取所有这些库。如果你想尝试更刺激的体验,还可以尝试一下 Quicklisp,这是一个方便的工具,可以帮助你管理 Common Lisp 依赖项。就像为你的网页服务器配备了一个私人采购员! 📦

    运行 Hunchentoot:启动引擎 💨

    现在你已经拥有了所有食材,是时候启动引擎了。你可以用一行代码启动一个基本的 Hunchentoot 服务器:

    (hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 4242))

    这将在端口 4242 上启动一个监听服务器。然后,你可以在浏览器中输入 http://127.0.0.1:4242/ 来访问它。就像打开你舒适小屋的门,欢迎访客一样。 🚪

    构建你的网页应用:添加家具和装饰 🛋️

    默认情况下,Hunchentoot 会从其源代码树中的 www/ 目录提供文件。想象一下,这就像你的空旷小屋,准备让你用自己的网页内容装饰。你可以使用 ACCEPTOR-DOCUMENT-ROOTACCEPTOR-ERROR-TEMPLATE-DIRECTORY 设置来定制文档根目录和错误模板目录的位置。

    想要为你的网页应用添加一些交互元素吗?Hunchentoot 提供了一个易于使用的框架来创建处理程序。你可以使用 DEFINE-EASY-HANDLER 宏来定义处理程序,它允许你创建响应特定请求的函数。就像在你的小屋里添加一张舒适的沙发和一个熊熊燃烧的壁炉一样。 🛋️🔥

    以下是一个简单的处理程序示例,它向访客说“你好”:

    (hunchentoot:define-easy-handler (say-yo :uri "/yo") (name)
      (setf (hunchentoot:content-type*) "text/plain")
      (format nil "Hey~@[ ~A~]!" name))

    现在,如果你在浏览器中访问 http://127.0.0.1:4242/yo,你将看到“你好!”。如果你访问 http://127.0.0.1:4242/yo?name=Dude,你将看到“你好,Dude!”。就像用友好的“你好!”来迎接你的客人一样。 👋

    Hunchentoot 位于代理服务器之后:添加一个门廊 🚪

    如果你对将你的网页应用暴露在狂野的互联网中感到有点害羞,你可以把它隐藏在一个代理服务器之后。想象一下,这就像在你的小屋前添加一个门廊,提供一层保护和隐私。

    一种常用的方法是使用 Apache 的 mod_proxy 模块。你可以配置它,将请求隧道到在特定端口上运行的 Hunchentoot 服务器。就像有一个友好的邻居为你开门一样。

    接受者:你的网页服务器的守门人 👮‍♀️

    接受者是你的 Hunchentoot 网页服务器的守门人。它们负责监听传入连接和处理请求。想象一下,它们就像你小屋里的友善员工,欢迎访客并确保一切顺利运行。

    Hunchentoot 提供了几种类型的接受者,包括标准的 ACCEPTOR 和用于安全连接的 SSL-ACCEPTOR。你可以通过子类化 ACCEPTOR 类并专门化定义其行为的通用函数来定制接受者的行为。就像在你的小屋的运作方式中添加你自己的个人风格一样。

    会话:跟踪你的访客 👤

    会话允许你跟踪你的访客和他们在你的网页应用上的活动。想象一下,它们就像一本留言簿,你可以记录谁来过你的小屋以及他们做了什么。

    Hunchentoot 提供了自动会话处理的功能,既有使用 cookie 的,也有不使用 cookie 的。你可以通过专门化相应的通用函数来定制会话行为。就像在与你的客人互动的方式中添加个性化的风格一样。

    日志记录:记录事件 📝

    日志记录对于理解你的网页应用的性能和排查任何问题至关重要。想象一下,它就像记录你小屋所有活动的一本日记。

    Hunchentoot 提供了访问和消息的日志记录功能。你可以通过设置 ACCEPTOR-ACCESS-LOG-DESTINATIONACCEPTOR-MESSAGE-LOG-DESTINATION 设置来定制日志记录行为。就像拥有一本日记,你可以记录你小屋的所有重要事件一样。

    错误处理:处理意外的客人 🚨

    即使是构建最好的网页应用,也可能会遇到意想不到的问题。这就是错误处理的用武之地。想象一下,它就像有一个计划来处理可能出现在你小屋的意外客人。

    Hunchentoot 提供了可定制的错误处理机制,允许你优雅地捕获和处理错误。你可以定义错误模板,为你的访客提供信息丰富的消息。就像为你的网页服务器准备了一个储备充足的急救箱一样。

    第三方插件:扩展你的小屋 🏘️

    Hunchentoot 是一款流行的网页服务器,有许多第三方库和框架可以扩展其功能。想象一下,它们就像你小屋的附加部分,比如一个舒适的秋千或一个宽敞的客房。

    一些流行的插件包括 Clack,一个可以与 Hunchentoot 一起使用的网页服务器抽象层,以及 hunchentoot-cgi,它为 Hunchentoot 提供 CGI 处理程序。还有一些框架,比如 RESTASCavemanRadianceSnooze,它们与 Hunchentoot 兼容。就像有一群朋友可以帮助你建造和维护你的小屋一样。

    结论:Hunchentoot – 你的 Common Lisp 网页服务器探险之旅 🏕️

    Hunchentoot 是一款功能强大且用途广泛的网页服务器,它为在 Common Lisp 中构建动态网站提供了坚实的基础。它就像森林里的一间舒适小屋,随时准备让你根据自己的喜好进行定制和装饰。所以,拿起你的工具,收集你的物资,开始构建你自己的网页杰作吧!

    参考文献:

    • Hunchentoot – The Common Lisp web server formerly known as TBNL (https://edicl.github.io/hunchentoot/)
    • Quicklisp (https://www.quicklisp.org/)
    • Clack (https://github.com/clack/clack)
    • hunchentoot-cgi (https://github.com/cyrus-harmon/hunchentoot-cgi)
    • RESTAS (https://github.com/edicl/restas)
    • Caveman (https://github.com/edicl/caveman)
    • Radiance (https://github.com/edicl/radiance)
    • Snooze (https://github.com/edicl/snooze)
    • Weblocks (https://github.com/weblocks/weblocks)
  • Quicklisp:你的 Common Lisp 库一站式商店

    🎉 Quicklisp:Common Lisp 的库管理器 🎉

    Quicklisp 就如同 Common Lisp 的终极包管理器,让你轻松下载、安装和加载库。想象一下,它是 Lisp 世界的“apt-get”或“pip”,但又多了一丝 Lisp 的魔法。

    🚀 Quicklisp:适用于所有 Lisp 的通用工具 🚀

    Quicklisp 与众多 Common Lisp 实现完美兼容,包括 ABCL、Allegro CL、Clasp、Clozure CL、CLISP、CMUCL、ECL、LispWorks、MKCL、SBCL 和 Scieneer CL。它就像你的 Lisp 项目的瑞士军刀,支持你在 Linux、Mac OS X 和 Windows 等不同平台上工作。

    📦 超过 1,500 个库触手可及 📦

    Quicklisp 为你提供了庞大的库生态系统,超过 1,500 个库随时待命,准备融入你的项目。这就像拥有一个装满工具的宝箱,每个工具都旨在解决特定问题或增强你的 Lisp 之旅。

    ✨ Quicklisp 入门:快速指南✨

    1. 下载并加载: 从 Quicklisp beta 网站下载 Quicklisp 文件(quicklisp.lisp)并将其加载到你的 Common Lisp 会话中。
    2. 验证完整性: 为了更安心,下载分离的 PGP 签名文件并将其与 Quicklisp 发布签名密钥进行验证。
    3. 安装 Quicklisp: 加载完成后,运行 (quicklisp-quickstart:install) 来安装 Quicklisp。这将下载并安装必要的文件,包括 ASDF,一个 Common Lisp 的包管理器。
    4. 开始使用 Quicklisp: 安装完成后,你可以使用 (ql:quickload "system-name") 加载库。例如,要加载 vecto 库,你可以使用 (ql:quickload "vecto")

    💡 Quicklisp 实战:真实世界示例 💡

    假设你正在使用 Common Lisp 构建一个 Web 应用程序。你需要一个库来处理 HTTP 请求。Quicklisp 闪亮登场!

    1. 找到合适的库: 使用 (ql:system-apropos "http") 搜索与 HTTP 相关的库。Quicklisp 将返回一个可用库列表。
    2. 加载库: 选择最符合你需求的库,并使用 (ql:quickload "library-name") 加载它。
    3. 开始编码: 现在你就可以使用库的函数和特性来构建你的 Web 应用程序了。

    🚀 超越基础:Quicklisp 高级功能 🚀

    • 卸载库: 使用 (ql:uninstall "system-name") 从系统中删除库。
    • 更新 Quicklisp: 使用 (ql:update-dist "quicklisp")(ql:update-client) 保持你的 Quicklisp 安装和库更新。
    • 查找依赖项: 使用 (ql:who-depends-on "system-name") 发现哪些库依赖于特定库。
    • SLIME 集成: Quicklisp 使得安装和配置 SLIME 变得轻而易举,SLIME 是 Emacs 的强大 Lisp 开发环境。

    🎉 Quicklisp:社区的共同努力 🎉

    Quicklisp 是 Common Lisp 社区协作精神的证明。它是 Zachary Beane 开发和维护的项目,并得到了众多个人的贡献。你可以加入 Quicklisp 讨论组邮件列表或访问 Freenode 上的 #quicklisp 频道,与其他 Quicklisp 用户交流。

    💻 Quicklisp:Common Lisp 开发的未来 💻

    Quicklisp 彻底改变了 Common Lisp 开发,使访问和使用库变得比以往更容易。随着 Common Lisp 生态系统的不断发展,Quicklisp 将在赋能开发人员构建创新且强大的应用程序方面发挥越来越重要的作用。

    参考资料:

  • CFFI 炼丹:用“点”化繁为简 🧙‍♂️

    引言

    CFFI,这个强大的工具,让我们在 Lisp 中调用 C 函数,如同驾驭风火轮,穿梭于两个世界。然而,使用 CFFI 的 API 编写 C 风格代码,有时却像是在泥潭中跋涉,因为你需要不断地传递类型信息,而 C 中的“点”运算符却拥有着神奇的类型推断能力,让我们可以轻松地访问结构体成员。

    为了让 CFFI 也能像 C 一样优雅,我们打造了 cffi-ops 这个炼丹炉,它将 CFFI 的繁琐操作,炼化为简洁的“点”操作,让你在 Lisp 中写 C 代码,如同行云流水般流畅。

    炼丹秘籍:规则与对比

    cffi-ops 的炼丹秘籍,就是将 C 中的“点”运算符,映射到 Lisp 中的宏定义,让 Lisp 代码的结构与 C 代码保持一致。

    C 语法cffi-ops 语法
    x->y.zx->y->z(-> x y z) (注意:xyz 必须与 defcstruct 中定义的符号相同)
    &x->y(& (-> x y))
    *x([] x)
    x[n]([] x n)
    &x[n]x + n(& ([] x n))
    x.y = z(setf (-> x y) z) 如果 z 是一个变量
    (csetf (-> x y) z) 如果 z 是一个 CFFI 指针
    A _a, *a = &_a(clet ((a (foreign-alloca '(:struct A. ))) ...)
    A *a = malloc(sizeof(A. )(clet ((a (cffi:foreign-alloc '(:struct A. ))) ...)
    A _a = *b, *a = &_a(clet ((a ([] b))) ...)
    A *a = b(clet ((a b)) ...)

    炼丹炉的奥妙:CFFI 指针与 clet

    在 Lisp 中,我们无法直接操作 C 的复合类型,因此,绑定和赋值复合类型需要借助 clet (或 clet*) 和 csetf,它们作用于 CFFI 指针,实现对 C 数据的操控。

    炼丹师的助手:arrow-macros

    cffi-ops 的炼丹炉,是建立在 arrow-macros 的基础上,它提供了 -> 宏,让我们可以像在 C 中一样,轻松地访问结构体成员。因此,cffi-opsarrow-macros 相辅相成,让你在 Lisp 中编写 C 代码,更加得心应手。

    炼丹实例:向量加法

    让我们来看一个 C 代码的例子:

    #include <stdlib.h>
    #include <assert.h>
    
    typedef struct {
      float x;
      float y;
      float z;
    } Vector3;
    
    typedef struct {
      Vector3 v1;
      Vector3 v2;
      Vector3 v3;  
    } Matrix3;
    
    void Vector3Add(Vector3 *output, const Vector3 *v1, const Vector3 *v2) {
      output->x = v1->x + v2->x;
      output->y = v1->y + v2->y;
      output->z = v1->z + v2->z;
    }
    
    int main(int argc, char *argv[]) {
      Matrix3 m1[3];
      m1[0].v1.x = 1.0;
      m1[0].v1.y = 2.0;
      m1[0].v1.z = 3.0;
      Matrix3 m2 = *m1;
      Vector3 *v1 = &m2.v1;
      Vector3 *v2 = malloc(sizeof(Vector3));
      ,*v2 = *v1;
      v2->x = 3.0;
      v2->z = 1.0;
      Vector3Add(v1, v1, v2);
      assert(v1->x == 4.0);
      assert(v1->y == 4.0);
      assert(v1->z == 4.0);
      free(v2);
      return 0;
    }

    使用 cffi-ops,我们可以将其改写为 Lisp 代码:

    (defpackage cffi-ops-example
      (:use #:cl #:cffi #:cffi-ops))
    
    (in-package #:cffi-ops-example)
    
    (defcstruct vector3
      (x :float)
      (y :float)
      (z :float))
    
    (defcstruct matrix3
      (v1 (:struct vector3))
      (v2 (:struct vector3))
      (v3 (:struct vector3)))
    
    (defun vector3-add (output v1 v2)
      (clocally
        (declare (ctype (:pointer (:struct vector3)) output v1 v2))
        (setf (-> output x) (+ (-> v1 x) (-> v2 x))
              (-> output y) (+ (-> v1 y) (-> v2 y))
              (-> output z) (+ (-> v1 z) (-> v2 z)))))
    
    (defun main ()
      (clet ((m1 (foreign-alloca '(:array (:struct matrix3) 3))))
        (setf (-> ([] m1 0) v1 x) 1.0
              (-> ([] m1 0) v1 y) 2.0
              (-> ([] m1 0) v1 z) 3.0)
        (clet* ((m2 ([] m1))
                (v1 (& (-> m2 v1)))
                (v2 (foreign-alloc '(:struct vector3))))
          (csetf ([] v2) ([] v1))
          (setf (-> v2 x) 3.0
                (-> v2 z) 1.0)
          (vector3-add v1 v1 v2)
          (assert (= (-> v1 x) 4.0))
          (assert (= (-> v1 y) 4.0))
          (assert (= (-> v1 z) 4.0))
          (foreign-free v2))))

    而没有使用 cffi-ops 的 Lisp 代码则更加冗长:

    (defpackage cffi-example
      (:use #:cl #:cffi))
    
    (in-package #:cffi-example)
    
    (defcstruct vector3
      (x :float)
      (y :float)
      (z :float))
    
    (defcstruct matrix3
      (v1 (:struct vector3))
      (v2 (:struct vector3))
      (v3 (:struct vector3)))
    
    (declaim (inline memcpy))
    (defcfun "memcpy" :void
      (dest :pointer)
      (src :pointer)
      (n :size))
    
    (defun vector3-add (output v1 v2)
      (with-foreign-slots (((xout x) (yout y) (zout z)) output (:struct vector3))
        (with-foreign-slots (((x1 x) (y1 y) (z1 z)) v1 (:struct vector3))
          (with-foreign-slots (((x2 x) (y2 y) (z2 z)) v2 (:struct vector3))
            (setf xout (+ x1 x2) yout (+ y1 y2) zout (+ z1 z2))))))
    
    (defun main ()
      (with-foreign-object (m1 '(:struct matrix3) 3)
        (with-foreign-slots ((x y z)
                             (foreign-slot-pointer
                              (mem-aptr m1 '(:struct matrix3) 0)
                              '(:struct matrix3) 'v1)
                             (:struct vector3))
          (setf x 1.0 y 2.0 z 3.0))
        (with-foreign-object (m2 '(:struct matrix3))
          (memcpy m2 m1 (foreign-type-size '(:struct matrix3)))
          (let ((v1 (foreign-slot-pointer m2 '(:struct matrix3) 'v1))
                (v2 (foreign-alloc '(:struct vector3))))
            (memcpy v2 v1 (foreign-type-size '(:struct vector3)))
            (with-foreign-slots ((x z) v2 (:struct vector3))
              (setf x 3.0 z 1.0))
            (vector3-add v1 v1 v2)
            (with-foreign-slots ((x y z) v1 (:struct vector3))
              (assert (= x 4.0))
              (assert (= y 4.0))
              (assert (= z 4.0)))
            (foreign-free v2)))))

    两种代码在 SBCL 上生成几乎相同的机器码,性能也十分接近。

    总结

    cffi-ops 为我们提供了一种简洁高效的方式,让我们在 Lisp 中编写 C 代码,如同在 C 中一样自然流畅。它将 CFFI 的复杂操作,炼化为简洁的“点”操作,让我们可以专注于代码的逻辑,而不用被繁琐的类型信息所困扰。

    参考文献

    1. cffi-ops
    2. arrow-macros
    3. CFFI
  • 🤔 Racket语言:自建DSL,编程新姿势

    💡 一种成熟且实用的语言

    Racket,就像一位技艺精湛的老工匠,经历了时间的考验,愈发成熟稳重。自诞生之初便专注于跨平台图形编程,无论是Windows、macOS还是Linux,都能在其支持下流畅运行。

    Racket不仅功能全面,而且拥有丰富的工具库,如同一个装满奇珍异宝的百宝箱,从构建Web服务器到进行数学和科学模拟,都能找到趁手的工具。

    特性描述
    包管理系统强大的包管理系统,轻松安装和管理第三方库,如同一个高效的物流系统,为你的项目提供源源不断的资源。
    GUI框架内置GUI框架,方便构建图形界面应用程序,如同一位技艺精湛的画师,让你的程序拥有赏心悦目的外观。
    独立二进制文件可生成独立运行的二进制文件,方便程序分发,如同将你的程序打包成精美的礼盒,可以轻松赠予他人。
    外部接口提供外部接口,方便与其他语言进行交互,如同一位优秀的翻译官,能够让你的程序与其他语言无障碍沟通。

    🚀 可扩展性:赋予你创造的力量

    Racket最令人着迷之处在于其强大的可扩展性,就像一块神奇的橡皮泥,你可以根据自己的需求随意塑造。它允许程序员使用宏来定义自己的语法结构,甚至可以创建全新的领域特定语言(DSL)。

    宏:程序员的魔法棒

    Racket的宏系统就像程序员手中的魔法棒,可以将重复的代码段抽象成简洁的语法结构,极大地提高了代码的表达力和可维护性。

    #lang racket
    (provide time-it)
    (require (for-syntax syntax/parse))
    
    (define-syntax (time-it stx)
      (syntax-parse stx
        [(_ task)
         #'(thunk-time-it (λ () task))]))
    
    (define (thunk-time-it task)
      (define before (cim))
      (define answer (task))
      (define delta  (- (cim) before))
      (printf "time: ~a ms\n" delta)
      answer)
    
    (define (cim current-inexact-milliseconds))

    例如,上面的代码展示了如何使用宏定义一个 time-it 语法,用于测量代码块的执行时间。使用 time-it 语法后,你就可以像使用普通函数一样来测量代码的执行时间,而无需编写重复的计时代码。

    DSL:为特定领域量身定制的语言

    Racket的宏系统强大到足以创建DSL,就像一位技艺高超的工程师,可以根据你的需求定制专属工具。通过DSL,你可以使用更简洁、更自然的语法来解决特定领域的问题。

    💪 健壮性:值得信赖的伙伴

    Racket非常注重程序的健壮性,它支持高阶软件契约和安全的渐进类型,就像一位经验丰富的安全专家,为你的程序保驾护航。

    ✨ 优雅的体验:编程也可以是一种享受

    Racket自带强大的IDE——DrRacket,它提供了一系列便捷的功能,例如代码自动补全、语法高亮、调试等等。DrRacket就像一位贴心的助手,让你在编程过程中事半功倍。

    🌐 Racket生态系统:丰富的资源和社区支持

    Racket拥有活跃的社区和丰富的学习资源,你可以在社区中与其他开发者交流经验,也可以从官方文档和书籍中学习Racket的各种特性。

    🎉 总结:开启编程新世界的大门

    Racket语言以其强大的可扩展性、健壮性和优雅的编程体验,为开发者打开了一扇通往编程新世界的大门。如果你厌倦了传统编程语言的繁琐和限制,不妨尝试一下Racket,相信它会带给你全新的编程体验。

    📚 参考文献

    1. https://racket-lang.org/
人生梦想 - 关注前沿的计算机技术 acejoy.com