Lisp 中 C 结构体的惯用等效项是什么?

发布于 2024-10-09 14:17:40 字数 533 浏览 0 评论 0原文

在 C 类型语言中,从一开始以及每本介绍性书籍都非常强调结构/记录和对象。然后,它们的完整系统是围绕管理这些结构、它们的相互关系和继承而设计的。

在 Lisp 文档中,您通常可以找到 1-2 页有关 Lisp 如何“也”具有 defstruct 的内容,这是一个简单的示例,通常就是这样。而且,结构的嵌套根本没有被提及。

对于来自 C 背景的人来说,首先似乎分层组织不同数据类型并不是 Lisp 中的首选方法,但除了 CLOS 之外,CLOS 是一个成熟的对象系统,如果您只想要结构,则太复杂,而且除了塞满所有内容之外到列表中,没有明显的方法来转移您的 C 结构知识。

最类似于 C 结构体的分层组织数据的惯用 Lisp 方式是什么?

--

我认为我的问题的总结答案是:对于初学者学习的目的,可以使用 defstruct 和/或 plists,尽管是“遗留功能”,因为它们与 C 结构最相似,但它们在很大程度上已被更灵活的 defclass/CLOS,现在大多数 Lisp 程序都使用它。

这是我关于 SO 的第一个问题,所以感谢大家花时间回答这个问题。

In C-type languages, there is a strong emphasis on structs/records and objects from the very beginning and in every introductory book. Then, their complete systems are designed around managing such structs, their mutual relations and inheritance.

In Lisp documentation, you can usually find 1-2 pages about how Lisp "also" has a defstruct, a simple example, and thats usually it. Also, nesting of structures is never mentioned at all.

For someone coming from a C background, it first seems that organizing different data types hierarchically isnt the preferred method in Lisp, but apart from CLOS, which is a full blown object system and too complicated if you just want structs, and apart from craming everything into lists, there isnt an apparent way to transfer your C struct knowledge.

What is the idiomatic Lisp way of hierarchically organizing data which most resembles C structs?

--

I think the summary answer to my question would be: For beginner learning purposes, defstruct and/or plists, although "legacy features", can be used, since they most closely resemble C structs, but that they have been largerly superseded by the more flexible defclass/CLOS, which what most Lisp programs use today.

This was my first question on SO, so thanks everyone for your time answering it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

天气好吗我好吗 2024-10-16 14:17:40

使用 CLOS。它并不复杂。

否则使用结构。

如果您对如何使用它们有具体疑问,请直接询问。

(defclass point ()
  ((x :type number)
   (y :type number)))

(defclass rectangle ()
  ((p1    :type point)
   (p2    :type point)
   (color :type color)))

类似的东西最终会导致像CLIM中的矩形(Common Lisp 接口管理器)。

历史

稍微扩展一下:历史上“结构”曾用于一些低级别的情况。结构具有单一继承性,并且槽访问是“快速”的。有些 Lisp 方言比 Common Lisp 提供的结构更多。然后从 70 年代中期开始,Lisp 开发了各种形式的面向对象表示形式。大多数结构化对象的表示形式从结构转移到某种面向对象的 Lisp 扩展。 80 年代流行的是基于类的系统,例如 Flavors、LOOPS 等。基于框架或基于原型的系统(例如 KEE Units 或 Object Lisp)也很流行。第一个 Macintosh Common Lisp 将 Object Lisp 用于其所有 UI 和 IO 设施。 MIT Lisp 机器基本上到处都使用了 Flavors。从 80 年代中期开始,ANSI CL 被开发出来。专门为 Common Lisp 开发了一个通用的面向对象系统:CLOS。它基于风味和循环。在那段时间里,除了实施者寻找改进实施的方法并提供浅层 CLOS 集成之外,几乎没有采取任何措施来真正改进结构。例如,结构不提供任何数据打包。如果有两个 4 位内容的槽,则无法指示 Common Lisp 将两个槽编码到单个 8 位内存区域中。

作为示例,您可以在 Lisp 机器手册中看到有关结构的章节( PDF),它的结构比 Common Lisp 提供的复杂得多。其中一些在 70 年代就已经存在于 Maclisp 中:Maclisp 手册中的 DEFSTRUCT

CLOS,Common Lisp 对象系统

大多数人都会同意 CLOS 是一个很好的设计。它有时会导致“更大”的代码,主要是因为标识符可能会变长。但是有一些 CLOS 代码,比如 AMOP 书中的代码,写得非常好,并且展示了它应该如何使用。

随着时间的推移,实施者必须应对开发人员想要使用 CLOS 的挑战,但也希望拥有结构的“速度”。对于“完整”CLOS 来说,这更是一项任务,其中包括用于 CLOS 的几乎标准的元对象协议 (MOP)。因此,实现者提供了一些技巧。在 80 年代,一些软件使用了开关,因此可以使用结构体或使用 CLOS - CLX 进行编译(低级 Common Lisp X11 接口就是一个例子)。原因是:在某些计算机和实现上,CLOS 比结构慢得多。今天提供这样的编译开关是不寻常的。

如果我今天看到一个好的 Common Lisp 实现,我会期望它几乎在所有地方都使用 CLOS。 STREAM 是 CLOS 类。 CONDITION 是 CLOS 类。 GUI 工具包使用 CLOS 类。小编使用的是CLOS。它甚至可能将外部类(例如 Objective C 类)集成到 CLOS 中。

在任何非玩具 Common Lisp 实现中,CLOS 都将成为提供结构化数据、通用行为和许多其他内容的工具。

正如其他一些答案中提到的,在某些地方可能不需要 CLOS。

Common Lisp 可以从一个函数返回多个值:

(defun calculate-coordinates (ship)
   (move-forward ship)
   (values (ship-x ship)
           (ship-y ship)))

可以在闭包中存储数据:

(defun create-distance-function (ship x y)
   (lambda ()
     (point-distance (ship-x ship) (ship-y ship) x y)))

对于配置,可以使用某种列表:

(defship ms-germany :initial-x 0 :initial-y 0)

你可以打赌我会在 CLOS 中实现船舶模型。

编写和维护 CLOS 软件的一个教训是,它需要精心设计,而且 CLOS 非常强大,人们可以用它创建非常复杂的软件 - 这种复杂性通常不是一个好主意。重构并简化!幸运的是,对于许多任务来说,基本的 CLOS 工具就足够了:DEFCLASS、DEFMETHOD 和 MAKE-INSTANCE。

CLOS 介绍指南

首先,Richard P. Gabriel 有他的CLOS 论文< /a> 下载。

另请参阅:

Use CLOS. It isn't complicated.

Otherwise use structures.

If you have a specific question how to use them, then just ask.

(defclass point ()
  ((x :type number)
   (y :type number)))

(defclass rectangle ()
  ((p1    :type point)
   (p2    :type point)
   (color :type color)))

Stuff like that eventually leads to interfaces like Rectangles in CLIM (the Common Lisp Interface Manager).

History

To expand on it a bit: Historically 'structures' have been used in some low-level situations. Structures have single inheritance and slot access is 'fast'. Some Lisp dialects have more to structures than what Common Lisp offers. Then from the mid-70s on various forms of object-oriented representations have been developed for Lisp. Most of the representation of structured objects moved from structures to some kind of object-oriented Lisp extension. Popular during the 80s were class-based systems like Flavors, LOOPS and others. Frame-based or prototype-based systems like KEE Units or Object Lisp were also popular. The first Macintosh Common Lisp used Object Lisp for all its UI and IO facilities. The MIT Lisp machine used Flavors basically everywhere. Starting in the mid 80s ANSI CL was developed. A common OO-system was developed especially for Common Lisp: CLOS. It was based on Flavors and Loops. During that time mostly nothing was done to really improve structures - besides implementors finding ways to improve the implementation and providing a shallow CLOS integration. For example structures don't provide any packing of data. If there are two slots of 4 bits content, there is no way to instruct Common Lisp to encode both slots into a single 8 bit memory region.

As an example you can see in the Lisp Machine Manual, chapter on structures (PDF), that it had much more complex structures than what Common Lisp provides. Some of that was already present in Maclisp in the 70s: DEFSTRUCT in the Maclisp manual.

CLOS, the Common Lisp Object System

Most people would agree that CLOS is a nice design. It sometimes leads to 'larger' code, mostly because identifiers can get long. But there is some CLOS code, like the one in the AMOP book, that is really nicely written and shows how it is supposed to be used.

Over time implementors had to deal with the challenge that developers wanted to use CLOS, but also wanted to have the 'speed' of structures. Which is even more a task with the 'full' CLOS, which includes the almost standard Meta Object Protocol (MOP) for CLOS. So there are some tricks that implementors provide. During the 80s some software used a switch, so it could compiled using structures or using CLOS - CLX (the low-level Common Lisp X11 interface was an example). The reason: on some computers and implementations CLOS was much slower than structures. Today it would be unusual to provide such a compilation switch.

If I look today at a good Common Lisp implementation, I would expect that it uses CLOS almost everywhere. STREAMs are CLOS classes. CONDITIONs are CLOS classes. The GUI toolkit uses CLOS classes. The editor uses CLOS. It might even integrate foreign classes (say, Objective C classes) into CLOS.

In any non-toy Common Lisp implementation CLOS will be the tool to provide structured data, generic behavior and a bunch of other things.

As mentioned in some of the other answers, in some places CLOS might not be needed.

Common Lisp can return more than one value from a function:

(defun calculate-coordinates (ship)
   (move-forward ship)
   (values (ship-x ship)
           (ship-y ship)))

One can store data in closures:

(defun create-distance-function (ship x y)
   (lambda ()
     (point-distance (ship-x ship) (ship-y ship) x y)))

For configuration one can use some kind of lists:

(defship ms-germany :initial-x 0 :initial-y 0)

You can bet that I would implement the ship model in CLOS.

A lesson from writing and maintaining CLOS software is that it needs to be carefully designed and CLOS is so powerful that one can create really complex software with it - a complexity which is often not a good idea. Refactor and simplify! Fortunately, for many tasks basic CLOS facilities are sufficient: DEFCLASS, DEFMETHOD and MAKE-INSTANCE.

Pointers to CLOS introductions

For a start, Richard P. Gabriel has his CLOS papers for download.

Also see:

夜清冷一曲。 2024-10-16 14:17:40

defstruct 的示例简短而简单,因为没有太多可说的。 C 的结构很复杂:

  • 内存管理
  • 由于联合、内联嵌套结构而导致内存布局复杂
    在 C 中,结构 还用于其他目的:

  • 访问内存

  • 由于缺乏多态性或传递“任意”类型值的能力而 因此通常会传递 void*
  • :它由于无法使用其他方式传递数据, 您可以传递一个包含所需数据的闭包
  • ;例如,在 Lisp 中,由于缺乏高级调用约定, ;一些函数在结构内部接受它们的参数

在 Common Lisp 中,defstruct 大致相当于 Java/C# 的 class:单继承、固定槽,可以用作 中的说明符defmethod(类似于虚拟方法)。结构完全适用于嵌套数据结构。

Lisp 程序往往不使用深层嵌套结构(Lisp 的源代码是主要例外),因为通常可以使用更简单的表示。

Examples with defstruct are short and simple because there isn't much to say about them. C's structs are complicated:

  • memory management
  • complicated memory layout due to unions, inline nested structures
    In C, structs are also used for other purposes:

  • to access memory

  • due to the lack of polymorphism or ability to pass a value of 'any' type: it is idiomatic to pass around a void*
  • due to inability to have other means of passing data; e.g., in Lisp you can pass a closure which has the needed data
  • due to the lack of advanced calling conventions; some functions accept their arguments inside structures

In Common Lisp, defstruct is roughly equivalent to Java's/C#'s class: single inheritance, fixed slots, can be used as specifiers in defmethods (analogous to virtual methods). Structures are perfectly usable for nested data structures.

Lisp programs tend not to use deeply nested structures (Lisp's source code being the primary exception) due to that often more simple representations are possible.

做个少女永远怀春 2024-10-16 14:17:40

我认为 C 结构体的惯用用法是不需要首先将数据存储在结构体中。我想说至少 50% 的 C 风格代码我已经移植到 Lisp,而不是将数据存储在某种复杂的结构中,我只是计算我想要计算的内容。 C 需要结构体来临时存储所有内容,因为它的表达式非常弱。

如果您有一些 C 风格代码的具体示例,我相信我们可以演示在 Lisp 中实现它的惯用方法。

除此之外,请记住 Lisp 的 s-exp 是分层数据。例如,Lisp 中的 if 表达式本身就是分层数据。

I think the idiomatic equivalent of a C struct is to not need to store the data in structs in the first place. I'd say at least 50% of the C-style code I've ported to Lisp, rather than storing the data in some elaborate structure, I just compute what it is I want to compute. C needs structs to store everything temporarily because its expressions are so weak.

If you a specific example of some C-style code, I'm sure we could demonstrate an idiomatic way to implement it in Lisp.

Beyond that, remember that Lisp's s-exps are hierarchical data. An if expression in Lisp, for example, is itself hierarchical data.

巨坚强 2024-10-16 14:17:40
(defclass point ()
          ( x y z))

(defun distance-from-origin (point)
               (with-slots (x y z)
                   point
                 (sqrt (+ (* x x) (* y y) (* z z)))))

我认为这就是我正在寻找的东西,可以在这里找到:

http: //cl-cookbook.sourceforge.net/clos-tutorial/index.html
y

(defclass point ()
          ( x y z))

(defun distance-from-origin (point)
               (with-slots (x y z)
                   point
                 (sqrt (+ (* x x) (* y y) (* z z)))))

I think this is kind of what I was looking for, can be found here:

http://cl-cookbook.sourceforge.net/clos-tutorial/index.html
y

旧情勿念 2024-10-16 14:17:40

为什么不使用哈希表?结构中的每个成员都可以是哈希表中的键。

Why not use hash tables? Every member in the struct can be a key in a hash table.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文