这似乎是我可以从我刚刚完成的如何设计程序(简化球拍)课程中获得的两件最重要的事情,直接来自课程的讲义:
1)尾部调用优化,以及非功能语言的缺乏:
可悲的是,大多数其他语言都不支持TAIL CALL 优化。换句话说,他们确实建立了一个堆栈 即使是尾巴电话。
尾部呼叫优化是在70年代中期发明的 在大多数语言的主要元素被开发之后。 因为他们没有尾调用优化,这些 语言提供了一组固定的LOOPING CONSTRUCTS 可以遍历任意大小的数据。
a)在没有特色的过程语言中,这种类型的优化有哪些等价物? b)使用那些等价物是否意味着我们避免在类似情况下在没有它的语言中建立堆栈?
2)突变和多核处理器
这种机制几乎可以用于任何其他语言 程序进入。我们推迟到目前为止推出它 有几个原因:
尽管是根本性的,但却非常复杂
过度使用它会导致程序无法使用 并行化(在多个处理器上运行)。 由于多核计算机现在普遍存在这种能力 仅在需要时使用突变变得越来越多 更重要的是
过度使用突变也会使其变得困难 理解程序,很难很好地测试它们
但可变变量很重要,并且学习这种机制 将为您提供更多准备工作Java,Python和许多 其他语言。即使在这些语言中,您也希望使用样式 被称为“主要是函数式编程”。
在学习本课程之前,我学习了一些Java,Python和C ++,因此认为变异是理所当然的。现在已经被上述声明全部抛到了空中。我的问题是:
a)我在哪里可以找到有关第二篇子弹中建议内容的更多详细信息,以及如何应对,以及 b)从“大多数功能性编程”风格中会出现什么样的模式,而不是我继续使用其他语言而不是采用这种课程时可能会有更粗心的风格?
答案 0 :(得分:9)
正如Leppie指出的那样,循环结构可以为它们支持的特定类型的循环设法恢复正确尾调用的空间节省。循环结构的唯一问题是你拥有的结构永远不够,除非你只是将球投入用户的球场并迫使他们明确地对堆栈进行建模。
举一个例子,假设您使用循环遍历二叉树。它有效...但你需要明确地跟踪“要回归的那些”。使用尾部调用语言进行递归遍历可以让你吃蛋糕并吃掉它,不用浪费空间,不要强迫你自己跟踪堆栈。
关于并行性和并发性的问题更加广泛,最好的指针可能是研究领域,而不是现有的解决方案。我认为大多数人会同意计算机世界正在发生危机;我们如何使我们的突变重编程技能适应新的多核心世界?
简单地转换到功能范例也不是这里的银弹;我们仍然不知道如何编写高级代码并生成超快速的非变异运行并发代码。不过,很多人都在努力解决这个问题!
答案 1 :(得分:4)
要扩展“可变性使并行性很难”的概念,当你有多个内核时,你必须使用同步,如果你想修改一个核心的东西,并让所有其他核心一致地看到它。
正确实现同步很难。如果过度同步,则会出现死锁,慢速(串行而不是并行)性能等。如果欠同步,则会发生部分观察到的更改(其他核心只看到你从不同核心所做的一部分改变),让你的对象被观察到一个无效的“中途改变”状态。
正是出于这个原因,许多函数式编程语言鼓励消息队列概念而不是共享状态概念。在这种情况下,唯一的共享状态是消息队列,并且在消息队列中管理同步是一个已解决的问题。
答案 2 :(得分:0)
a)在没有特色的过程语言中,这种类型的优化有哪些等价物? b)使用那些等价物是否意味着我们避免在类似情况下在没有它的语言中建立堆栈?
好吧,尾调用的意义在于它可以在不添加到调用堆栈的情况下评估另一个函数,因此构建堆栈的任何东西都不能真正被称为等价。
尾部调用的行为基本上类似于跳转到新代码,使用函数调用的语言陷阱和所有适当的详细信息管理。因此,在没有这种优化的语言中,您可以在单个函数中使用跳转。如果没有其他工作,则循环,条件块甚至任意goto
语句。
a)我在哪里可以找到有关第二篇子弹中建议内容的更多详细信息,以及如何处理
第二颗子弹听起来过于简单化了。有许多方法可以使并行化变得比它需要的更困难,并且过度使用变异只是一种。
但是,请注意并行化(将任务拆分为可以同时完成的部分)与并发性完全相同(可以同时执行可以交互的多个任务),尽管肯定存在重叠。避免变异 在编写并发程序时非常有用,因为不可变数据可以避免很多竞争条件和资源争用,否则这些都是可能的。
b)从“大多数功能性编程”风格中会出现什么样的模式,而不是我继续使用其他语言而不是采用这种课程时可能会有更粗心的风格?
你看过Haskell还是Clojure?两者都倾向于强调控制突变的非常功能性的风格。 Haskell对此更加严谨,但是有很多工具可以处理有限形式的可变性,而Clojure有点非正式,可能对你更熟悉,因为它是另一种Lisp方言。