无限列表对任何现实世界的应用程序都有用吗?

发布于 2024-10-08 15:27:15 字数 162 浏览 10 评论 0原文

我已经使用 Haskell 有一段时间了,并且我已经阅读了《Real World Haskell》和《Learn You a Haskell》的大部分内容。我想知道的是,使用惰性求值的语言是否有意义,特别是拥有无限列表的“优势”,是否存在无限列表使任务变得非常容易的任务,甚至是只有无限列表才能完成的任务清单?

I've been using haskell for quite a while now, and I've read most of Real World Haskell and Learn You a Haskell. What I want to know is whether there is a point to a language using lazy evaluation, in particular the "advantage" of having infinite lists, is there a task which infinite lists make very easy, or even a task that is only possible with infinite lists?

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

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

发布评论

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

评论(8

凉城已无爱 2024-10-15 15:27:15

这是一个非常琐碎但实际上日常有用的示例,说明无限列表特别有用:当您有一个项目列表想要用来初始化某些键值样式的数据结构时,从连续的键开始。因此,假设您有一个字符串列表,并且希望将它们放入从 0 开始计数的 IntMap 中。如果没有惰性无限列表,您将执行诸如遍历输入列表之类的操作,并保持运行“下一个索引”计数器并构建 IntMap

对于无限惰性列表,列表本身充当运行计数器的角色;只需使用 zip [0..] 与项目列表来分配索引,然后使用 IntMap.fromList 构建最终结果。

当然,这两种情况本质上是一样的。但是,使用惰性无限列表可以让您更直接地表达概念,而不必担心输入列表的长度或跟踪额外计数器等细节。

Here's an utterly trivial but actually day-to-day useful example of where infinite lists specifically come in handy: When you have a list of items that you want to use to initialize some key-value-style data structure, starting with consecutive keys. So, say you have a list of strings and you want to put them into an IntMap counting from 0. Without lazy infinite lists, you'd do something like walk down the input list, keeping a running "next index" counter and building up the IntMap as you go.

With infinite lazy lists, the list itself takes the role of the running counter; just use zip [0..] with your list of items to assign the indices, then IntMap.fromList to construct the final result.

Sure, it's essentially the same thing in both cases. But having lazy infinite lists lets you express the concept much more directly without having to worry about details like the length of the input list or keeping track of an extra counter.

呆头 2024-10-15 15:27:15

一个明显的例子是将数据处理从输入链接到您想要用它执行的任何操作。例如,将字符流读入惰性列表,由词法分析器处理,同时生成标记的惰性列表,将其解析为惰性 AST 结构,然后编译并执行。这就像使用 Unix 管道一样。

An obvious example is chaining your data processing from input to whatever you want to do with it. E.g., reading a stream of characters into a lazy list, which is processed by a lexer, also producing a lazy list of tokens which are parsed into a lazy AST structure, then compiled and executed. It's like using Unix pipes.

那小子欠揍 2024-10-15 15:27:15

我发现在一个地方定义所有序列通常更容易、更清晰,即使它是无限的,并且让使用它的代码只获取它想要的东西。

take 10 mySequence
takeWhile (<100) mySequence

而不是使用大量相似但不完全相同的函数来生成子集。

first10ofMySequence
elementsUnder100ofMySequence

当同一序列的不同子部分用于不同区域时,好处会更大。

I found it's often easier and cleaner to just define all of a sequence in one place, even if it's infinite, and have the code that uses it just grab what it wants.

take 10 mySequence
takeWhile (<100) mySequence

instead of having numerous similar but not quite the same functions that generate a subset

first10ofMySequence
elementsUnder100ofMySequence

The benefits are greater when different subsections of the same sequence are used in different areas.

云醉月微眠 2024-10-15 15:27:15

正如所解释的那样,无限的数据结构(包括列表)极大地提高了模块化性,从而提高了可重用性。 John Hughes 的经典论文为什么函数式编程很重要< /a>.
例如,您可以将复杂的代码块分解为生产者/过滤器/消费者片段,每个片段在其他地方都可能有用。

因此,只要您看到代码重用的现实价值,您的问题就会得到答案。

Infinite data structures (including lists) give a huge boost to modularity and hence reusability, as explained & illustrated in John Hughes's classic paper Why Functional Programming Matters.
For instance, you can decompose complex code chunks into producer/filter/consumer pieces, each of which is potentially useful elsewhere.

So wherever you see real-world value in code reuse, you'll have an answer to your question.

琉璃繁缕 2024-10-15 15:27:15

基本上,惰性列表允许您延迟计算直到需要它为止。当您事先不知道何时停止以及要预先计算什么时,这可能会很有用。

一个标准示例是 u_n 收敛到某个极限的一系列数值计算。您可以要求第一项满足 |u_n - u_{n-1}| < epsilon,会为您计算正确的项数。

现在,您有两个这样的序列 u_n 和 v_n,并且您想知道 epsilon 精度限制的总和。该算法是:

  • 计算 u_n 直到 epsilon/2 精度
  • 计算 v_n 直到 epsilon/2 精度
  • 返回 u_n + v_n

所有都是惰性完成的,只计算必要的 u_n 和 v_n。您可能需要不太简单的示例,例如。在您知道(即知道如何计算)f 的连续模量的情况下计算 f(u_n)。

Basically, lazy lists allow you to delay computation until you need it. This can prove useful when you don't know in advance when to stop, and what to precompute.

A standard example is u_n a sequence of numerical computations converging to some limit. You can ask for the first term such that |u_n - u_{n-1}| < epsilon, the right number of terms is computed for you.

Now, you have two such sequences u_n and v_n, and you want to know the sum of the limits to epsilon accuracy. The algorithm is:

  • compute u_n until epsilon/2 accuracy
  • compute v_n until epsilon/2 accuracy
  • return u_n + v_n

All is done lazily, only the necessary u_n and v_n are computed. You may want less simple examples, eg. computing f(u_n) where you know (ie. know how to compute) f's modulus of continuity.

黯然 2024-10-15 15:27:15

声音合成 - 请参阅 Jerzy Karczmarczuk 的这篇论文:

http://users.info。 unicaen.fr/~karczma/arpap/cleasyn.pdf

Jerzy Karczmarcuk 还有许多其他论文使用无限列表来模拟幂级数和导数等数学对象。

我已经将基本的声音合成代码翻译成 Haskell - 足以用于正弦波单元生成器和 WAV 文件 IO。性能足以在 1.5GHz Athalon 上运行 GHCi - 因为我只是想测试这个概念,所以从未抽出时间来优化它。

Sound synthesis - see this paper by Jerzy Karczmarczuk:

http://users.info.unicaen.fr/~karczma/arpap/cleasyn.pdf

Jerzy Karczmarcuk has a number of other papers using infinite lists to model mathematical objects like power series and derivatives.

I've translated the basic sound synthesis code to Haskell - enough for a sine wave unit generator and WAV file IO. The performance was just about adequate to run with GHCi on a 1.5GHz Athalon - as I just wanted to test the concept I never got round to optimizing it.

梦旅人picnic 2024-10-15 15:27:15

无限/惰性结构允许“打结”的习语: http://www.haskell.org/haskellwiki /Tying_the_Knot

典型的简单示例是斐波那契数列,直接定义为递推关系。 (是的,是的,举行效率投诉/算法讨论 - 重点是惯用法。):fibs = 1:1:zipwith (+) fibs (tail fibs)

这是另一个故事。我有一些只适用于有限流的代码——它做了一些事情来将它们创建到一个点,然后做了一大堆废话,涉及对依赖于该点之前的整个流的流的各个位进行操作,将它与来自另一个流的信息合并,等等。这非常好,但我意识到它有一大堆处理边界条件所必需的东西,以及基本上当一个流用完东西时该怎么办。然后我意识到,从概念上讲,它没有理由不能在无限流上工作。所以我切换到没有 nil 的数据类型——即真正的流而不是列表,所有的麻烦都消失了。尽管我知道我永远不会需要超过某个点的数据,但能够依赖它的存在使我能够安全地删除许多愚蠢的逻辑,并让我的代码的数学/算法部分更加清晰地突出。

Infinite/lazy structures permit the idiom of "tying the knot": http://www.haskell.org/haskellwiki/Tying_the_Knot

The canonically simple example of this is the Fibonacci sequence, defined directly as a recurrence relation. (Yes, yes, hold the efficiency complaints/algorithms discussion -- the point is the idiom.): fibs = 1:1:zipwith (+) fibs (tail fibs)

Here's another story. I had some code that only worked with finite streams -- it did some things to create them out to a point, then did a whole bunch of nonsense that involved acting on various bits of the stream dependent on the entire stream prior to that point, merging it with information from another stream, etc. It was pretty nice, but I realized it had a whole bunch of cruft necessary for dealing with boundary conditions, and basically what to do when one stream ran out of stuff. I then realized that conceptually, there was no reason it couldn't work on infinite streams. So I switched to a data type without a nil -- i.e. a genuine stream as opposed to a list, and all the cruft went away. Even though I know I'll never need the data past a certain point, being able to rely on it being there allowed me to safely remove lots of silly logic, and let the mathematical/algorithmic part of my code stand out more clearly.

月下凄凉 2024-10-15 15:27:15

我最喜欢的实用主义之一是cyclecycle [False, True] 生成无限列表[False, True, False, True, False ...]。特别是,xs! 0 = 假xs! 1 = True,所以这只是表示元素的索引是否为奇数。这出现在哪里?有很多地方,但这里有一个任何 Web 开发人员都应该熟悉的地方:制作逐行交替着色的表格。

这里看到的一般模式是,如果我们想在有限列表上执行某些操作,而不是必须构造一个特定的有限列表来“做我们想要的事情”,我们可以使用一个适用于所有大小的无限列表列表。 camcann 的回答正是如此。

One of my pragmatic favorites is cycle. cycle [False, True] generates the infinite list [False, True, False, True, False ...]. In particular, xs ! 0 = False, xs ! 1 = True, so this is just says whether or not the index of the element is odd or not. Where does this show up? Lot's of places, but here's one that any web developer ought to be familiar with: making tables that alternate shading from row to row.

The general pattern seen here is that if we want to do some operation on a finite list, rather than having to construct a specific finite list that will “do the thing we want,” we can use an infinite list that will work for all sizes of lists. camcann’s answer is in this vein.

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