为什么更高阶的程序?

时间:2009-05-07 02:44:38

标签: 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

类似的任务可以用不支持高阶过程的语言来完成,方法是定义Proc,取4个而不是3个参数,并调用此过程来定义ProcA,如:

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

那么为什么有关高阶程序的模糊呢?我错过了什么吗?

8 个答案:

答案 0 :(得分:8)

很好地观察到返回另一个函数的函数与带有两个参数的函数相同。这被称为“Currying”。换句话说,从A到B的函数是逻辑蕴涵的证明,A暗示B或:

A => B.

如你所知,如果A暗示B暗示C,则A和B暗示C或:

(A => (B => C)) <==> ((A, B) => C)

但是更高阶函数不一定是返回另一个函数的函数。 高阶函数是一个以另一个函数作为参数的函数。这是一个重要的区别,HOF是非常强大的编程工具。

例如,考虑一下这个Haskell函数:

map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x : (map f xs)

此高阶函数接受函数f并将其应用于列表中的每个元素。在没有HOF的语言中,你可以使用循环或类似的东西来执行此功能,但是在具有HOF的语言中,您可以通过这样的简单调用为列表中的每个元素调用f

map f myList

当然,语言中的控件构造可以让您接近高阶函数,但是具有高阶函数的语言可以让您创建自己的控件构造。方案当然有资格。

答案 1 :(得分:3)

我不会试图在这里重述这个论点,但在Why Functional Programming Matters中,John Hughes认为高阶函数是有用的,因为它们提供了更有效的方法来“粘合”程序的各个部分,从而它们使重用代码变得更容易。这些例子是一种非常古老的语言,不再使用太多,但它们仍然易于遵循并且非常有说服力。阅读约翰的论文是一个很好的方法来详细解答你的问题“为什么有关于高阶程序的这么多模糊”。

答案 2 :(得分:1)

这更多是关于心态而不是可行性。它允许您将功能视为一等公民,并根据功能操作来创建其他功能等。

显然你可以用其他语言来做或模拟它,但是如果它不是一种句法机制,它会被视为一种加法或黑客攻击。

答案 3 :(得分:1)

好的,但在第二个示例中,您在编译时使用预先设定的a1b1c1列表创建该过程。在第一个示例中,当您调用ProcA时,您将在运行时创建它,并且您可以根据需要创建任意多个不同的内容,这样您就可以做更多有趣的事情。

答案 4 :(得分:1)

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

说,您使用以下程序原型编写排序算法:

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

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

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

一旦C#支持这一点,我知道它现在更加主流。 :)

答案 7 :(得分:0)

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

(define (foo) (+ 2 3)) ;; (foo) => 5

这是一个无聊的功能,它总是会增加2到3.如果我们将其概括,那么它不仅会增加2到3,而是添加到任何用户提供的数字?

(define (foo n) (+ 2 n)) ;; (foo 10) => 12

当一种语言不支持高阶函数时,你不得不认为函数和值(例如数字,布尔值,列表)是两个不同的东西。但functional programming(FP)模糊了它们之间的区别。想象一下,函数和值之间的唯一区别在于可以调用函数,除了可以对2#t'(a b c)执行任何功能的函数。 :您可以将其作为参数,或从函数返回,或存储在变量中,或将其放在列表中。例如,让我们进一步概括我们的小函数,因此它不仅可以将{2}添加到n,而是将{2}乘以n,或者应用任何其他可接受两个数字的函数: / p>

(define (foo f n) (f 2 n))
;; (foo + 10) => 12
;; (foo * 10) => 20
;; (foo expt 10) => 1024

当你意识到一个函数的处理方式与处理数字或字符串的方式相同时,anonymous functions(在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))).

因此,HOF允许我们概括我们的功能,使其超级灵活。如果你看看你的功能,看看它背后的逻辑,你可以意识到,如果某些东西对你的数据进行操作,那么其他东西也可能。如果你将2个数字加在一起,你可以将它们相乘,或相减,或取偶数或其他任何数字。而不是每次为每个案例编写一个新函数,你可以接受一个额外的参数,它必须是一个函数。

在FP中,我们一直使用HOF,例如,在操作列表时。 3个功能是FP的面包和黄油:mapfilterfoldlmap接受带有1个参数的函数,将此函数应用于列表的每个元素,并返回包含已更改元素的新列表。 filter接受带有1个参数的谓词(返回布尔值的函数),将谓词应用于列表的每个元素,并返回一个新列表,其中的元素不满足删除的谓词。

(map (lambda (n) (+ n 1)) '(1 2 3 4 5) ;; '(2 3 4 5 6)
(define (foo n) (+ n 1))
(map foo '(1 2 3 4 5)) ;; '(2 3 4 5 6)
(filter (lambda (n) (> n 3)) '(1 2 3 4 5)) ;; '(4 5)
(define (bar n) (> n 3))
(filter bar '(1 2 3 4 5)) ;; '(4 5)

想象一下,你有一个1-arity函数列表 - 再次,你可以用函数做任何你想做的事情,并将它存储在数据结构中 - 你想要将它们全部应用到相同的数字,并且获取结果列表。

(let ((xs (list (lambda (x) (+ x 1))
                (lambda (x) (* x 2))
                (lambda (x) (- x)))))
  (map (lambda (f) (f 10)) xs)) ;; => (11 20 -10)

结论:当编程语言正确支持函数式编程概念时,高阶函数允许灵活性和通用性,这使得代码更强大(您可以对各种用例使用相同的函数) )和简洁(无需编写一个函数的10个版本)。一些高阶函数在函数式编程中被大量使用,因此您可以摆脱低级和冗长的for循环,并编写完成所有操作的单行函数。

注意: foldl,与“left fold”或“left reduce”相同,功能更强大。如果您真的感兴趣并有时间,请阅读my answer using reduce的前半部分。虽然它不是为Scheme / Racket编写的,但是对于Common Lisp / Emacs Lisp,你仍然可以理解fold / reduce背后的想法。