高阶函数有哪些有趣的用途?

Man*_*áoz 33 haskell functional-programming function combinators higher-order-functions

我目前正在做一个函数式编程课程,我对高阶函数和函数作为一等公民的概念感到很开心.但是,我还不能想到许多实用的,概念上令人惊奇的,或者只是简单有趣的高阶函数.(除了典型的和相当乏味map,filter等函数).

你知道这些有趣功能的例子吗?

也许返回函数的函数,返回函数列表的函数(?)等.

我很欣赏Haskell中的例子,这是我目前正在学习的语言:)

Don*_*art 45

好吧,你注意到Haskell没有循环语法?没有whiledofor.因为这些只是高阶函数:

 map :: (a -> b) -> [a] -> [b]

 foldr :: (a -> b -> b) -> b -> [a] -> b

 filter :: (a -> Bool) -> [a] -> [a]

 unfoldr :: (b -> Maybe (a, b)) -> b -> [a]

 iterate :: (a -> a) -> a -> [a]
Run Code Online (Sandbox Code Playgroud)

高阶函数替代了控制结构语言中语法烘焙的需要,这意味着几乎每个Haskell程序都使用这些函数 - 使它们非常有用!

它们是实现良好抽象的第一步,因为我们现在可以将自定义行为插入到通用骨架函数中.

特别是,monad是唯一可能的,因为我们可以链接在一起,并操纵函数来创建程序.

事实是,当它是一阶时,生活很无聊.只有高阶编程才能使编程变得有趣.

  • 非常好的答案.人们提出的一些"控制结构"非常传统但非常有用.我想到的一个很好的例子是`quickCheck` - 一个控制操作符,它使用任意输入多次运行提供的函数,试图使其失败.它是一个如此简单的概念,高阶函数(和类型类)使它使用起来同样简单.只需`quickCheck $ \n xs - > length(取n xs)<= n`并观察(那是一个假的,顺便说一句,对于负面的'n` - 一个容易疏忽,但quickCheck容易抓住它). (4认同)
  • 好吧,monad跟随,然后更高阶函数超过monad.这是乌龟一路走来! (3认同)
  • 刚想到的另一个(它不是立即因为我倾向于认为它是理所当然的)是'forkIO',或其他任何与并发有关的东西.产生一个线程是一个非常有用的高阶函数,人们甚至在C中使用它,尽管语言缺乏对它们的任何特殊支持.只要看看`pthread_create`的类型.HOF非常有用,人们愿意忍受定义参数结构,摆弄指针,通常手动管理它们的内存,以及来自`void*`的转换,只是为了能够在C中使用它们. (2认同)

sig*_*fpe 37

面向对象编程中使用的许多技术都是缺乏高阶函数的解决方法.

这包括许多在函数式编程中无处不在的设计模式.例如,访问者模式是实现折叠的一种相当复杂的方式.解决方法是使用方法创建一个类,并将类的元素作为参数传入,作为传入函数的替代.

策略模式是经常通过对象作为参数对于什么是真正目的,功能的替代方案的另一个例子.

类似地,依赖注入通常涉及一些笨重的方案来传递函数的代理,而简单地将函数直接作为参数传递通常会更好.

所以我的答案是,高阶函数通常用于执行OO程序员执行的相同类型的任务,但是直接执行,并且使用很少的样板.

  • 正如旁注,OO没有任何关于将函数作为第一类对象的功能.SmallTalk和Ruby都具有一流的功能. (7认同)

luq*_*qui 15

当我学习一个函数时,我真的开始觉得它的力量可以成为数据结构的一部分.这是一个"消费者monad"(technobabble:免费monad (i ->)).

data Coro i a
    = Return a
    | Consume (i -> Coro i a)
Run Code Online (Sandbox Code Playgroud)

所以a Coro可以立即产生一个值,或者根据某些输入成为另一个Coro.例如,这是一个Coro Int Int:

Consume $ \x -> Consume $ \y -> Consume $ \z -> Return (x+y+z)
Run Code Online (Sandbox Code Playgroud)

这会消耗三个整数输入并返回它们的总和.您还可以根据输入使其行为不同:

sumStream :: Coro Int Int
sumStream = Consume (go 0)
    where
    go accum 0 = Return accum
    go accum n = Consume (\x -> go (accum+x) (n-1))
Run Code Online (Sandbox Code Playgroud)

这会消耗一个Int,然后在产生它们的总和之前消耗更多的Int.这可以被认为是一个函数,它接受任意多个参数,没有任何语言魔法,只是更高阶函数.

在我开始做Haskell之前,数据结构中的函数是一个非常强大的工具,不属于我的词汇表.


yat*_*975 11

查看论文'甚至更高阶的解析函数或者为什么有人想要使用六阶函数?' 作者:Chris Okasaki.它是用ML编写的,但这些想法同样适用于Haskell.


Kur*_*sis 9

Joel Spolsky写了一篇名的文章,演示了Map-Reduce如何使用Javascript的高阶函数.任何问这个问题的人都必须阅读.


C. *_*ann 7

curask还需要高阶函数,Haskell在任何地方都使用它.本质上,一个带两个参数的函数相当于一个函数接受一个参数并返回另一个函数接受一个参数.当你在Haskell中看到这样的类型签名时:

f :: A -> B -> C
Run Code Online (Sandbox Code Playgroud)

... (->)可以读作右关联,表明这实际上是一个返回类型函数的高阶函数B -> C:

f :: A -> (B -> C)
Run Code Online (Sandbox Code Playgroud)

两个参数的非curried函数将改为具有如下类型:

f' :: (A, B) -> C
Run Code Online (Sandbox Code Playgroud)

因此,只要您在Haskell中使用部分应用程序,就可以使用更高阶的函数.


Rus*_*nor 6

MartínEscardó提供了一个有关高阶函数的有趣例子:

equal :: ((Integer -> Bool) -> Int) -> ((Integer -> Bool) -> Int) -> Bool
Run Code Online (Sandbox Code Playgroud)

给定两个函f, g :: (Integer -> Bool) -> Int,然后equal f g决定是否fg是(外延上)等于或不,即使fg没有一个有限区域.实际上,codomain Int可以被具有可判定等式的任何类型替换.

Escardó给出的代码是用Haskell编写的,但是相同的算法应该适用于任何函数式语言.

您可以使用Escardó描述的相同技术来计算任意连续函数的任意精度的定积分.