Programming languages “什么东西”是什么意思;“写得好吗?”;?

Programming languages “什么东西”是什么意思;“写得好吗?”;?,programming-languages,functional-programming,clojure,composition,Programming Languages,Functional Programming,Clojure,Composition,很多时候,我都会遇到这种形式的语句 X写得不好 我记得最近读到的几个例子: 宏编写不好(上下文:clojure) 锁组合不好(上下文:clojure) 命令式编程不能很好地组合。。。等等 我想从设计/阅读/编写代码的角度理解可组合性的含义?示例会很好。组合(在您在功能级别描述的上下文中)通常是将一个函数干净地馈送到另一个函数的能力,而无需中间处理。C++中的std::cout就是这样一个组合示例: cout“砰砰的一声”——好的构图意味着每个构图规则都有很高的表现力比率。每个宏都介绍自己的组

很多时候,我都会遇到这种形式的语句 X写得不好

我记得最近读到的几个例子:

  • 宏编写不好(上下文:clojure)
  • 锁组合不好(上下文:clojure)
  • 命令式编程不能很好地组合。。。等等
我想从设计/阅读/编写代码的角度理解可组合性的含义?示例会很好。

组合(在您在功能级别描述的上下文中)通常是将一个函数干净地馈送到另一个函数的能力,而无需中间处理。C++中的std::cout就是这样一个组合示例:

cout“砰砰的一声”——好的构图意味着每个构图规则都有很高的表现力比率。每个宏都介绍自己的组合规则。每个自定义数据结构都执行相同的操作。函数,尤其是那些使用通用数据结构的函数,其规则要少得多


赋值和其他副作用,尤其是wrt并发性有更多的规则。

在编写函数或方法时,请考虑一下。您可以创建一组功能来执行特定任务。在使用面向对象的语言时,您将您的行为聚集在您认为系统中的不同实体将执行的操作周围。功能性程序通过鼓励作者根据抽象对功能进行分组而摆脱了这一点。例如,Clojure环库包含一组涉及web应用程序中路由的抽象

环是可组合的,其中描述系统中路径的函数(路由)可以分组为高阶函数(中间位置)。事实上,Clojure是如此的动态,以至于有可能(也鼓励您)提出可以在运行时动态创建的路由模式。这是复合性的本质,而不是提出解决特定问题的模式,而是关注生成特定问题解决方案的模式。生成器和代码生成器只是函数式编程中常用的两种模式。函数编程是生成其他模式(等等)的模式艺术


其思想是在最基本的层次上解决问题,然后提出解决问题的最低层次函数的模式或组。一旦你开始看到最低层次的模式,你就发现了组合。当人们在函数组中发现二阶模式时,他们可能会开始看到第三个层次。等等…

在clojure的上下文中,讨论了可组合性的某些方面。一般来说,当系统的各个单元做好一件事,不需要其他单元了解其内部结构,避免副作用,并接受和返回系统的普遍数据结构时,就会出现这种情况。所有这些都可以在M2TM的C++例子中看到。

“组成”函数基本上就是指将两个或多个函数结合在一起,从而形成一个大的函数,它们以有用的方式组合了它们的功能。本质上,您定义了一系列函数,并将每个函数的结果传递到下一个函数,最后给出整个过程的结果。Clojure提供了

comp
功能来为您执行此操作,您也可以手动执行


通常,可以创造性地与其他函数链接的函数比只能在特定条件下调用的函数更有用。例如,如果我们没有
last
函数,而只有传统的Lisp列表函数,那么我们可以很容易地将
last
定义为
(def last(comp first reverse))
。看看这个-我们甚至不需要
defn
或提及任何参数,因为我们只是将一个函数的结果传输到另一个函数中。例如,如果reverse采取了修改顺序的强制路线,那么这将不起作用。宏也是有问题的,因为您无法将它们传递给函数,如
comp
apply
,编程中的组合意味着用较小的块组装较大的块

  • 一元函数的组合通过链接更简单的一元函数来创建更复杂的一元函数
  • 控制流构造的组合将控制流构造放置在其他控制流构造中
  • 数据结构的组合将多个简单的数据结构组合成一个更复杂的数据结构
理想情况下,组合单元的工作方式与基本单元类似,作为程序员,您不需要意识到其中的差异。如果事情没有达到理想状态,或者某个东西编写得不好,那么您编写的程序可能没有其各个部分的(预期)组合行为

假设我有一些简单的C代码

void run_with_resource(void) { Resource *r = create_resource(); do_some_work(r); destroy_resource(r); } 使用\u资源运行\u(void){ 资源*r=创建_资源(); 做一些工作(r); 破坏_资源(r); } C有助于在函数级别对控制流进行组合推理。我不必关心里面到底发生了什么做些什么工作;通过查看这个小函数,我知道每次使用create_resource()在第2行创建资源时,它最终都会在第4行被destroy_resource()销毁

嗯,不完全是。如果create_resource()获得锁,而destroy_resource()释放锁,该怎么办?然后我不得不担心是否有些工作获得了相同的锁,这将阻止函数完成。如果有人调用了longjmp(),并且完全跳过了我函数的结尾,该怎么办?在我知道do_some_work()中发生了什么之前,我无法预测函数的控制流。我们不再具有组合性:我们不再能够将程序分解为各个部分,独立地对各个部分进行推理,并将我们的结论带回整个程序。这使得设计和调试成为可能