Programming languages 为什么是高阶程序?

Programming languages 为什么是高阶程序?,programming-languages,functional-programming,scheme,closures,abstraction,Programming Languages,Functional Programming,Scheme,Closures,Abstraction,所以,若一种语言提供了高阶过程,那个么我可以有一个返回过程的过程。比如: (define (Proc a b c) (lambda (x) ( #| method body here in terms of a b c and x |# ))) (define ProcA (Proc a1 b1 c1)) ; Would create ProcA that has 1 argument 要创建新的过程,我只需执行以下操作: (define (Proc a b c) (lambda (

所以,若一种语言提供了高阶过程,那个么我可以有一个返回过程的过程。比如:

(define (Proc a b c)
  (lambda (x) ( #| method body here in terms of a b c and x |# )))
(define ProcA (Proc a1 b1 c1)) ; Would create ProcA that has 1 argument
要创建新的过程,我只需执行以下操作:

(define (Proc a b c)
  (lambda (x) ( #| method body here in terms of a b c and x |# )))
(define ProcA (Proc a1 b1 c1)) ; Would create ProcA that has 1 argument
类似的任务可以在不支持高阶过程的语言中完成,方法是定义采用4而不是3个参数的
Proc
,并调用此过程来定义
ProcA
,如:

(define (Proc a b c x) ( #| method body -- does not return any procedure |# )
(define (ProcA x) (Proc a1 b1 c1 x))

那么,为什么高阶过程有如此多的模糊性呢?我遗漏了什么吗?

这更多的是关于心态而不是可行性。它允许您将函数视为一等公民,并根据对函数进行操作以创建其他函数的函数进行思考,等等


显然,您可以用其他语言来实现或模拟这一点,但如果这不是一种语法机制,它会被视为一种加法或黑客。

好的,但在第二个示例中,您在编译时使用预先指定的
a1
b1
c1
列表创建该过程。在第一个示例中,当您调用
ProcA
时,您正在运行时创建它,您可以创建任意多个不同的,这样您就可以做更多有趣的事情。

想想变换函数或通过数组的排序算法。现在,您想让它变得非常灵活,让函数的用户通过将函数作为参数传递来指定函数的行为

比方说,您使用以下程序原型编写了一个排序算法:

sort(Array a, void (*fn)(a::element_type, a::element_type));

该函数的用户可以通过传递适当的fn来指定他们是否需要降序或升序。

我不想在这里重述这个论点,但在中,John Hughes认为高阶函数很有用,因为它们提供了更有效的方法来“粘合”程序的各个部分,因此,它们使重用代码变得更容易。这些例子使用的是一种非常古老的语言,不再经常使用,但它们仍然很容易理解,并且非常有说服力。阅读John的论文是一个很好的方法,可以详细回答您的问题“为什么高阶过程有这么多模糊性”。

一个很好的观察结果是,返回另一个函数的函数与接受两个参数的函数相同。这就是所谓的“咖喱”。换句话说,从a到B的函数是逻辑蕴涵的证明,即a意味着B,或:

A => B.
正如您所注意到的,如果A意味着B意味着C,那么A和B意味着C,或者:

(A => (B => C)) <==> ((A, B) => C)
此高阶函数接受函数
f
,并将其应用于列表中的每个元素。在没有HOFs的语言中,您可以使用循环或类似的方法来执行此函数,但在有HOFs的语言中,您可以通过以下简单调用来调用列表中的每个元素的
f

map f myList

当然,语言中的控件构造允许您近似高阶函数,但具有高阶函数的语言允许您发明自己的控件构造。Scheme当然合格。

您需要一个内部类来正确模拟它。第一种情况下,Proc在a、b和c上闭合。在第二种情况下,ProcA的调用方无法控制a1、b1和c1如何传递给另一个过程,他只能控制x。因此,控制a1、b1和c1的方式是通过在更高范围(模块级别或类似级别)使用变量,这使得函数不纯粹。在这种情况下,您无法确保在调用之间给定相同的参数,ProcA将返回相同的结果。与Proc一样,如果使用相同的参数调用它,则始终可以确保会产生相同的结果。

我在javascript中使用高阶函数,例如,当我使用选择框时。我可以传入选择某个选项时将调用的函数,因为对我来说唯一的区别是,它简化了代码,减少了冗余

我在我使用的其他支持高阶函数的语言中也看到了同样的情况,因为我可以开始研究如何清理代码,其中存在一些可以本地化的冗余,任何差异都可以在函数中完成


一旦C#支持了这一点,我就知道它现在更加主流了

如果函数接受和/或返回一个函数,则称为(HOF)。对于缺乏经验的程序员来说,来自C、C++或java的高阶函数听起来像魔术,但是它们非常简单。想象一个返回2+3结果的简单函数:

(define (foo) (+ 2 3)) ;; (foo) => 5
这是一个无聊的函数,它总是加2到3。如果我们对其进行推广,使其不仅将2添加到3,而且将2添加到任何用户提供的数字,会怎么样

(define (foo n) (+ 2 n)) ;; (foo 10) => 12
当一种语言不支持高阶函数时,你不得不认为函数和值(如数字、布尔值、列表)是两种截然不同的东西。但是(FP)模糊了它们之间的区别。想象一下,函数和值之间的唯一区别是可以调用函数,而不是可以对函数执行
2
#t
'(ab c)
:您可以将其作为参数给出,或从函数返回,或存储在变量中,或将其放入列表中。例如,让我们进一步概括我们的小函数,这样它不仅可以将2添加到
n
,还可以将2乘以
n
,或者应用任何其他可以接受两个数字的函数:

(define (foo f n) (f 2 n))
;; (foo + 10) => 12
;; (foo * 10) => 20
;; (foo expt 10) => 1024
当您意识到一个函数可以用处理数字或字符串的相同方式来处理时,(在FP术语中称为“lambdas”)就完全有意义了。匿名函数实际上比普通命名函数更基本和“正常”,命名函数只是放入变量中的匿名函数,就像我们将数字放入变量中多次使用它一样

(+ 2 2) ;; is no different from:
(let ((a 2)) (+ a a))
(lambda (x y) (* x y)) ;; is no different from:
(define (foo x y) (* x y)) ;; which is an abbreviation for:
(define foo (lambda (x y) (* x y))).
所以HOFs允许我们推广我们的函数,使它们超级灵活。如果你看看你的函数,看看它背后的逻辑,你会意识到,如果某个东西在y上运行
(let ((xs (list (lambda (x) (+ x 1))
                (lambda (x) (* x 2))
                (lambda (x) (- x)))))
  (map (lambda (f) (f 10)) xs)) ;; => (11 20 -10)