我是否需要了解 Haskell 如何表示数据才能编写好的 Haskell 程序?

发布于 2024-09-25 19:17:03 字数 226 浏览 7 评论 0原文

我正在从 Java 背景学习 Haskell。当我编写 Java 程序时,我觉得我对对象在内存中的布局及其后果有深入的了解。例如,我确切地知道 java.lang.Stringjava.util.LinkedList 是如何工作的,因此我知道应该如何使用它们。对于 Haskell,我有点迷失了。例如,(:) 是如何工作的?我应该关心吗?是否在某处指定?

I'm learning Haskell from a Java background. When I program Java, I feel like I have a strong understanding of how objects are laid out in memory and the consequences of this. For example I know exactly how java.lang.String and java.util.LinkedList work and therefore I know how I should use them. With Haskell I'm a bit lost. For example, how does (:) work? Should I care? Is it specified somewhere?

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

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

发布评论

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

评论(2

鹿港小镇 2024-10-02 19:17:03

简短的回答是否定的。在 Haskell 中编程时,您应该将数据结构视为纯数学对象,而不用担心它们在内存中的表示方式。原因是,在没有副作用的情况下,除了创建数据的函数以及可用于提取构造数据的更简单部分的函数之外,实际上没有任何数据。 。

要查看有关 (:) 等数据构造函数或任何其他术语的信息,请使用 :type (或简称为 :t)命令GHCi 内部:

:Prelude> :type (:)
(:) :: a -> [a] -> [a]

这告诉您 (:) 构造函数(发音为“cons”)接受任何类型的值和相同类型的列表,并返回相同类型的列表。您还可以使用 :info 命令获取更多信息。这将向您展示数据定义的样子:

Prelude> :info (:)
data [] a = ... | a : [a]   -- Defined in GHC.Types
infixr 5 :

这告诉您 (:) 是将元素添加到现有列表的构造函数。

我还强烈推荐 Hoogle 不仅可以按名称查找内容,还可以进行相反的搜索;您知道您正在寻找的函数的签名,并且想要查找是否有人已经为您编写了该函数。 Hoogle 很好,因为它提供了描述和示例用法。

归纳数据的形状

我上面说过,了解数据在内存中的表示并不重要...但是,您应该了解您正在处理的数据的形状,以避免糟糕的结果绩效决策。 Haskell 中的所有数据都是归纳定义的,这意味着它具有树状形状,不断向外递归展开。您可以通过查看数据的定义来判断数据的形状;一旦您知道如何阅读此内容,它的性能特征就真的没有什么隐藏的了:

data MyList a = Nil | Cons a (MyList a)

正如您从定义中看到的,获得新的 MyList 的唯一方法是通过 Cons 构造函数。如果你多次使用这个构造函数,你最终会得到大致如下形状的东西:

(Cons a5 (Cons a4 (Cons a3 (Cons a2 (Cons a1 Nil)))))

它只是一棵没有分支的树,这就是列表的定义!获得a1的唯一方法是依次弹出每个Cons;因此,访问最后一个元素的时间复杂度为O(n),而访问头部的时间复杂度为常数。一旦您可以根据数据结构的定义对数据结构进行此类推理,您就一切就绪了。

The short answer is no. When programming in Haskell you should think of your data structures as pure mathematical objects and not worry about how they're represented in memory. The reason for this is that, in the absence of side-effects, there's really nothing to data except the functions that create it and the functions you can use to extract the simpler parts out of which it was constructed.

To see information about data constructors like (:), or any other terms, use the :type (or just :t for short) command inside GHCi:

:Prelude> :type (:)
(:) :: a -> [a] -> [a]

That tells you that the (:) constructor (pronounced "cons"), takes a value of any type, and a list of the same type, and returns a list of the same type. You can also get a bit more info by using the :info command. This will show you what the data definition looks like:

Prelude> :info (:)
data [] a = ... | a : [a]   -- Defined in GHC.Types
infixr 5 :

This tells you that (:) is the constructor which prepends an element to an existing list.

I also highly recommend Hoogle not only for looking up things by name but for doing the reverse kind of search; where you know the signature of the function you're looking for and want to find if someone's already written it for you. Hoogle is nice because it gives descriptions and example usages.

Shapes of Inductive Data

I said above that it's not important to know the representation of your data in memory... you should, however, understand the shape of the data you're dealing with, to avoid poor performance decisions. All data in Haskell is inductively defined, meaning it has a tree-like shape which unfolds ever outwards recursively. You can tell the shape of data by looking at its definition; there's really nothing hidden about its performance characteristics once you know how to read this:

data MyList a = Nil | Cons a (MyList a)

As you can see from the definition, the only way you can get a new MyList is by the Cons constructor. If you use this constructor multiple times, you'll end up with something roughly of this shape:

(Cons a5 (Cons a4 (Cons a3 (Cons a2 (Cons a1 Nil)))))

It's just a tree with no branches, that's the definition of a list! And the only way to get at a1 is by popping off each of the Conss in turn; hence access to the last element is O(n), whereas access to the head is constant time. Once you can do this kind of reasoning about data structures based on their definitions, you're all set.

︶ ̄淡然 2024-10-02 19:17:03

简短的回答是“不”,您不需要了解数据布局——但了解复杂性会很有用。

然而,要编写高度优化的 Haskell 程序,对堆中数据结构的形状有充分的了解是必不可少的。一个可以帮助实现此目的的工具是 "vacuum",它可以渲染 Haskell 数据结构的图表正如它们所布置的那样。

一些示例:

$ view "hello"

链接列表

$ view (IntMap.fromList $ zip [1..10] [1..])

Data.IntMap

以下是有关如何使用该工具的简短视频:http:// /www.youtube.com/watch?v=X4-212uMgy8

Short answer is "no", you don't need to know about data layouts -- a knowledge of complexity will be useful though.

To write highly optimized Haskell programs, a good working knowledge of the shape of data structures in the heap is essential, however. A tool to help this is "vacuum", which can render diagrams of Haskell data structures as they are laid out.

Some examples:

$ view "hello"

linked lists

$ view (IntMap.fromList $ zip [1..10] [1..])

Data.IntMap

Here's a short video of how to use the tool: http://www.youtube.com/watch?v=X4-212uMgy8

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