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

发布于 2024-11-03 13:47:24 字数 256 浏览 5 评论 0原文

我目前正在学习函数式编程课程,我对高阶函数和一等公民函数的概念感到非常有趣。然而,我还想不出许多实际有用的、概念上令人惊奇的或只是简单有趣的高阶函数。 (除了典型且相当沉闷的 mapfilter 等功能)。

你知道这样有趣的函数的例子吗?

也许是返回函数的函数、返回函数列表的函数(?)等等。

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

I'm currently doing a Functional Programming course and I'm quite amused by the concept of higher-order functions and functions as first class citizens. However, I can't yet think of many practically useful, conceptually amazing, or just plain interesting higher-order functions. (Besides the typical and rather dull map, filter, etc functions).

Do you know examples of such interesting functions?

Maybe functions that return functions, functions that return lists of functions (?), etc.

I'd appreciate examples in Haskell, which is the language I'm currently learning :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

简美 2024-11-10 13:47:27

这是一个小的释义 代码 片段:

rays :: ChessPieceType -> [[(Int, Int)]]
rays Bishop = do
  dx <- [1, -1]
  dy <- [1, -1]
  return $ iterate (addPos (dx, dy)) (dx, dy)
...  -- Other piece types

-- takeUntilIncluding is an inclusive version of takeUntil
takeUntilIncluding :: (a -> Bool) -> [a] -> [a]

possibleMoves board piece = do
  relRay <- rays (pieceType piece)
  let ray = map (addPos src) relRay
  takeUntilIncluding (not . isNothing . pieceAt board)
    (takeWhile notBlocked ray)
  where
    notBlocked pos =
      inBoard pos &&
      all isOtherSide (pieceAt board pos)
    isOtherSide = (/= pieceSide piece) . pieceSide

这使用了几个“更高阶" 函数:

iterate :: (a -> a) -> a -> [a]
takeUntilIncluding  -- not a standard function
takeWhile :: (a -> Bool) -> [a] -> [a]
all :: (a -> Bool) -> [a] -> Bool
map :: (a -> b) -> [a] -> [b]
(.) :: (b -> c) -> (a -> b) -> a -> c
(>>=) :: Monad m => m a -> (a -> m b) -> m b

(.). 运算符,(>>=)do - 符号“换行符”。

在 Haskell 中编程时,你只需使用它们。当您没有高阶函数时,您就会意识到它们有多么有用。

Here's a small paraphrased code snippet:

rays :: ChessPieceType -> [[(Int, Int)]]
rays Bishop = do
  dx <- [1, -1]
  dy <- [1, -1]
  return $ iterate (addPos (dx, dy)) (dx, dy)
...  -- Other piece types

-- takeUntilIncluding is an inclusive version of takeUntil
takeUntilIncluding :: (a -> Bool) -> [a] -> [a]

possibleMoves board piece = do
  relRay <- rays (pieceType piece)
  let ray = map (addPos src) relRay
  takeUntilIncluding (not . isNothing . pieceAt board)
    (takeWhile notBlocked ray)
  where
    notBlocked pos =
      inBoard pos &&
      all isOtherSide (pieceAt board pos)
    isOtherSide = (/= pieceSide piece) . pieceSide

This uses several "higher order" functions:

iterate :: (a -> a) -> a -> [a]
takeUntilIncluding  -- not a standard function
takeWhile :: (a -> Bool) -> [a] -> [a]
all :: (a -> Bool) -> [a] -> Bool
map :: (a -> b) -> [a] -> [b]
(.) :: (b -> c) -> (a -> b) -> a -> c
(>>=) :: Monad m => m a -> (a -> m b) -> m b

(.) is the . operator, and (>>=) is the do-notation "line break operator".

When programming in Haskell you just use them. Where you don't have the higher order functions is when you realize just how incredibly useful they were.

空心↖ 2024-11-10 13:47:27

这是一种我还没有看到其他人提到过的模式,当我第一次了解到它时,它真的让我感到惊讶。考虑一个统计包,其中您有一个样本列表作为输入,并且您想要计算它们的一堆不同的统计数据(还有很多其他方法来激发这一点)。最重要的是,您有一个要运行的函数列表。你如何运行它们?

statFuncs :: [ [Double] -> Double ]
statFuncs = [minimum, maximum, mean, median, mode, stddev]

runWith funcs samples = map ($samples) funcs

这里发生了各种各样的高阶善良,其中一些已经在其他答案中提到过。但我想指出“$”功能。当我第一次看到“$”的这种用法时,我惊呆了。在此之前,除了作为括号的方便替代品之外,我并不认为它非常有用......但这几乎是神奇的......

Here's a pattern that I haven't seen anyone else mention yet that really surprised me the first time I learned about it. Consider a statistics package where you have a list of samples as your input and you want to calculate a bunch of different statistics on them (there are also plenty of other ways to motivate this). The bottom line is that you have a list of functions that you want to run. How do you run them all?

statFuncs :: [ [Double] -> Double ]
statFuncs = [minimum, maximum, mean, median, mode, stddev]

runWith funcs samples = map ($samples) funcs

There's all kinds of higher order goodness going on here, some of which has been mentioned in other answers. But I want to point out the '$' function. When I first saw this use of '$', I was blown away. Before that I hadn't considered it to be very useful other than as a convenient replacement for parentheses...but this was almost magical...

樱桃奶球 2024-11-10 13:47:27

一件有趣的事情,如果不是特别实用的话,就是教堂数字。这是一种仅使用函数来表示整数的方法。疯了,我知道。这是我制作的 JavaScript 实现。它可能比 Lisp/Haskell 实现更容易理解。 (但说实话,可能不是。JavaScript 并不是真正适合这种事情的。)

One thing that's kind of fun, if not particularly practical, is Church Numerals. It's a way of representing integers using nothing but functions. Crazy, I know. <shamelessPlug>Here's an implementation in JavaScript that I made. It might be easier to understand than a Lisp/Haskell implementation. (But probably not, to be honest. JavaScript wasn't really meant for this kind of thing.)</shamelessPlug>

鹤舞 2024-11-10 13:47:27

有人提到 Javascript 支持某些高阶函数,包括 Joel Spolsky 的一篇文章< /a>. Mark Jason Dominus 写了一整本书,名为 Higher–Order Perl;该书的源代码可以多种格式免费下载,包括 PDF。

至少从 Perl 3 开始,Perl 支持的功能更像是 Lisp,而不是 C,但直到 Perl 5 才完全支持闭包以及随之而来的所有功能。第一个 Perl 6 实现是用 Haskell 编写的,这对该语言的设计进展产生了很大的影响。

Perl 中函数式编程方法的示例出现在日常编程中,尤其是使用 mapgrep 时:

@ARGV    = map { /\.gz$/ ? "gzip -dc < $_ |" : $_ } @ARGV;

@unempty = grep { defined && length } @many;

由于 sort 也允许闭包,因此 >map/sort/map 模式非常常见:

@txtfiles = map { $_->[1] }
            sort { 
                    $b->[0]  <=>     $a->[0]
                              ||
                 lc $a->[1]  cmp  lc $b->[1]
                              ||
                    $b->[1]  cmp     $a->[1]
            }
            map  { -s => $_ } 
            grep { -f && -T }
            glob("/etc/*");

或者

@sorted_lines = map { $_->[0] }
                sort {
                     $a->[4] <=> $b->[4] 
                             ||
                    $a->[-1] cmp $b->[-1]
                             ||
                     $a->[3] <=> $b->[3]
                             ||
                     ...
                }
                map { [$_ => reverse split /:/] } @lines;

reduce 函数使列表黑客变得容易而无需循环:

$sum = reduce { $a + $b } @numbers;

$max = reduce { $a > $b ? $a : $b } $MININT, @numbers;

还有更多,但这只是一种尝试。闭包可以轻松创建函数生成器,编写您自己的高阶函数,而不仅仅是使用内置函数。事实上,最常见的异常模型之一

try {
   something();
} catch {
   oh_drat();
};

不是内置的。然而,它几乎被简单地定义为一个带有两个参数的函数:第一个参数中的闭包和第二个参数中带有闭包的函数。

Perl 5 没有内置柯里化,尽管有一个模块。然而,Perl 6 内置了柯里化和一流的延续,还有更多。

It’s been mentioned that Javascript supports certain higher-order functions, including an essay from Joel Spolsky. Mark Jason Dominus wrote an entire book called Higher–Order Perl; the book’s source is available for free download in a variety of fine formats, include PDF.

Ever since at least Perl 3, Perl has supported functionality more reminiscent of Lisp than of C, but it wasn’t until Perl 5 that full support for closures and all that follows from that was available. And ne of the first Perl 6 implementations was written in Haskell, which has had a lot of influence on how that language’s design has progressed.

Examples of functional programming approaches in Perl show up in everyday programming, especially with map and grep:

@ARGV    = map { /\.gz$/ ? "gzip -dc < $_ |" : $_ } @ARGV;

@unempty = grep { defined && length } @many;

Since sort also admits a closure, the map/sort/map pattern is super common:

@txtfiles = map { $_->[1] }
            sort { 
                    $b->[0]  <=>     $a->[0]
                              ||
                 lc $a->[1]  cmp  lc $b->[1]
                              ||
                    $b->[1]  cmp     $a->[1]
            }
            map  { -s => $_ } 
            grep { -f && -T }
            glob("/etc/*");

or

@sorted_lines = map { $_->[0] }
                sort {
                     $a->[4] <=> $b->[4] 
                             ||
                    $a->[-1] cmp $b->[-1]
                             ||
                     $a->[3] <=> $b->[3]
                             ||
                     ...
                }
                map { [$_ => reverse split /:/] } @lines;

The reduce function makes list hackery easy without looping:

$sum = reduce { $a + $b } @numbers;

$max = reduce { $a > $b ? $a : $b } $MININT, @numbers;

There’s a lot more than this, but this is just a taste. Closures make it easy to create function generators, writing your own higher-order functions, not just using the builtins. In fact, one of the more common exception models,

try {
   something();
} catch {
   oh_drat();
};

is not a built-in. It is, however, almost trivially defined with try being a function that takes two arguments: a closure in the first arg and a function that takes a closure in the second one.

Perl 5 doesn’t have have currying built-in, although there is a module for that. Perl 6, though, has currying and first-class continuations built right into it, plus a lot more.

玩心态 2024-11-10 13:47:26

好吧,您注意到 Haskell 没有 for 循环语法吗?没有 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]

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

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

特别是,monad之所以成为可能,是因为我们可以链接在一起并操作函数来创建程序。

事实上,当生活处于一阶状态时,它是相当无聊的。只有当你拥有更高的阶数时,编程才会变得有趣。

Well, you notice that Haskell has no syntax for loops? No while or do or for. Because these are all just higher-order functions:

 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]

Higher-order functions replace the need for baked in syntax in the language for control structures, meaning pretty much every Haskell program uses these functions -- making them quite useful!

They are the first step towards good abstraction because we can now plug custom behavior into a general purpose skeleton function.

In particular, monads are only possible because we can chain together, and manipulate functions, to create programs.

The fact is, life is pretty boring when it is first-order. Programming only gets interesting once you have higher-order.

绝對不後悔。 2024-11-10 13:47:26

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

这包括许多在函数式编程中普遍存在的设计模式。例如,访问者模式是实现折叠的相当复杂的方法。解决方法是创建一个带有方法的类,并传入该类的元素作为参数,以代替传入函数。

策略 模式是另一个方案示例,该方案经常将对象作为参数传递,以代替实际预期的函数。

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

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

Many techniques used in OO programming are workarounds for the lack of higher order functions.

This includes a number of the design patterns that are ubiquitous in functional programming. For example, the visitor pattern is a rather complicated way to implement a fold. The workaround is to create a class with methods and pass in an element of the class in as an argument, as a substitute for passing in a function.

The strategy pattern is another example of a scheme that often passes objects as arguments as a substitute for what is actually intended, functions.

Similarly dependency injection often involves some clunky scheme to pass a proxy for functions when it would often be better to simply pass in the functions directly as arguments.

So my answer would be that higher-order functions are often used to perform the same kinds of tasks that OO programmers perform, but directly, and with a lot less boilerplate.

翻了热茶 2024-11-10 13:47:26

当我了解到函数可以成为数据结构的一部分时,我真正开始感受到它的力量。这是一个“消费者 monad”(技术问题:(i ->) 上的免费 monad)。

data Coro i a
    = Return a
    | Consume (i -> Coro i a)

因此,Coro 可以立即产生一个值,或者根据某些输入成为另一个 Coro。例如,这是一个 Coro Int Int

Consume $ \x -> Consume $ \y -> Consume $ \z -> Return (x+y+z)

它消耗三个整数输入并返回它们的总和。您还可以让它根据输入有不同的行为:

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

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

数据结构中的函数是一个非常强大的工具,在我开始使用 Haskell 之前,它并不属于我的词汇范围。

I really started to feel the power when I learned a function can be part of a data structure. Here is a "consumer monad" (technobabble: free monad over (i ->)).

data Coro i a
    = Return a
    | Consume (i -> Coro i a)

So a Coro can either instantly yield a value, or be another Coro depending on some input. For example, this is a Coro Int Int:

Consume $ \x -> Consume $ \y -> Consume $ \z -> Return (x+y+z)

This consumes three integer inputs and returns their sum. You could also have it behave differently according to the inputs:

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

This consumes an Int and then consumes that many more Ints before yielding their sum. This can be thought of as a function that takes arbitrarily many arguments, constructed without any language magic, just higher order functions.

Functions in data structures are a very powerful tool that was not part of my vocabulary before I started doing Haskell.

那小子欠揍 2024-11-10 13:47:26

查看论文 'Even High-Order Functions for Parsing 或 Why Will任何人都想
使用六阶函数?'
作者:Chris Okasaki。它是使用 ML 编写的,但这些想法同样适用于 Haskell。

Check out the paper 'Even Higher-Order Functions for Parsing or Why Would Anyone Ever Want To
Use a Sixth-Order Function?'
by Chris Okasaki. It's written using ML, but the ideas apply equally to Haskell.

待"谢繁草 2024-11-10 13:47:26

Joel Spolsky 写了一篇著名文章,展示了如何Map-Reduce 使用 Javascript 的高阶函数进行工作。对于任何提出这个问题的人来说,这都是必读的。

Joel Spolsky wrote a famous essay demonstrating how Map-Reduce works using Javascript's higher order functions. A must-read for anyone asking this question.

星軌x 2024-11-10 13:47:26

Haskell 随处使用的柯里化也需要高阶函数。本质上,接受两个参数的函数相当于接受一个参数的函数并返回另一个接受一个参数的函数。当你在 Haskell 中看到这样的类型签名时:

f :: A -> B -> C

...(->) 可以被解读为右关联,表明这实际上是一个返回函数的高阶函数输入 B -> C

f :: A -> (B -> C)

两个参数的非柯里化函数将具有如下类型:

f' :: (A, B) -> C

因此,每当您在 Haskell 中使用部分应用程序时,您都在使用高阶函数。

Higher-order functions are also required for currying, which Haskell uses everywhere. Essentially, a function taking two arguments is equivalent to a function taking one argument and returning another function taking one argument. When you see a type signature like this in Haskell:

f :: A -> B -> C

...the (->) can be read as right-associative, showing that this is in fact a higher-order function returning a function of type B -> C:

f :: A -> (B -> C)

A non-curried function of two arguments would instead have a type like this:

f' :: (A, B) -> C

So any time you use partial application in Haskell, you're working with higher-order functions.

司马昭之心 2024-11-10 13:47:26

Martín Escardó 提供了一个有趣的高阶函数示例:

equal :: ((Integer -> Bool) -> Int) -> ((Integer -> Bool) -> Int) -> Bool

给定两个泛函 f, g :: (Integer -> Bool) -> Int,然后 equal f g 决定 fg 是否(扩展地)相等,即使 f< /code> 和 g 没有有限域。事实上,余域 Int 可以替换为任何具有可判定相等性的类型。

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

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

Martín Escardó provides an interesting example of a higher-order function:

equal :: ((Integer -> Bool) -> Int) -> ((Integer -> Bool) -> Int) -> Bool

Given two functionals f, g :: (Integer -> Bool) -> Int, then equal f g decides if f and g are (extensionally) equal or not, even though f and g don't have a finite domain. In fact, the codomain, Int, can be replaced by any type with a decidable equality.

The code Escardó gives is written in Haskell, but the same algorithm should work in any functional language.

You can use the same techniques that Escardó describes to compute definite integrals of any continuous function to arbitrary precision.

玩套路吗 2024-11-10 13:47:26

您可以做的一件有趣且有点疯狂的事情是使用函数模拟面向对象的系统并将数据存储在函数的范围内(即在闭包中)。它是高阶的,因为对象生成器函数是返回对象的函数(另一个函数)。

我的 Haskell 相当生疏,所以我不能轻易给你一个 Haskell 示例,但这里有一个简化的 Clojure 示例,希望能够传达这个概念:

(defn make-object [initial-value]
  (let [data (atom {:value initial-value})]
      (fn [op & args]
        (case op 
          :set (swap! data assoc :value (first args))
          :get (:value @data)))))

用法:

(def a (make-object 10))

(a :get)
=> 10

(a :set 40)

(a :get)
=> 40

相同的原理在 Haskell 中也适用(除了你可能需要更改 set 操作)返回一个新函数,因为 Haskell 是纯函数式的)

One interesting and slightly crazy thing you can do is simulate an object-oriented system using a function and storing data in the function's scope (i.e. in a closure). It's higher-order in the sense that the object generator function is a function which returns the object (another function).

My Haskell is rather rusty so I can't easily give you a Haskell example, but here's a simplified Clojure example which hopefully conveys the concept:

(defn make-object [initial-value]
  (let [data (atom {:value initial-value})]
      (fn [op & args]
        (case op 
          :set (swap! data assoc :value (first args))
          :get (:value @data)))))

Usage:

(def a (make-object 10))

(a :get)
=> 10

(a :set 40)

(a :get)
=> 40

Same principle would work in Haskell (except that you'd probably need to change the set operation to return a new function since Haskell is purely functional)

请叫√我孤独 2024-11-10 13:47:26

我特别喜欢高阶记忆化:(

memo :: HasTrie t => (t -> a) -> (t -> a)

给定任何函数,返回该函数的记忆化版本。受限于函数的参数必须能够编码到 trie 中的事实。)

这来自 < a href="http://hackage.haskell.org/package/MemoTrie" rel="nofollow">http://hackage.haskell.org/package/MemoTrie

I'm a particular fan of higher-order memoization:

memo :: HasTrie t => (t -> a) -> (t -> a)

(Given any function, return a memoized version of that function. Limited by the fact that the arguments of the function must be able to be encoded into a trie.)

This is from http://hackage.haskell.org/package/MemoTrie

自由范儿 2024-11-10 13:47:26

这里有几个例子: http://www.haskell.org/haskellwiki/Higher_order_function

我会还推荐这本书:http://www.cs.nott.ac.uk/~gmh/book.html 这是对所有 Haskell 的精彩介绍,涵盖了高阶函数。

高阶函数通常使用累加器,因此可以在从较大列表中形成符合给定规则的元素列表时使用。

There are several examples here: http://www.haskell.org/haskellwiki/Higher_order_function

I would also recommend this book: http://www.cs.nott.ac.uk/~gmh/book.html which is a great introduction to all of Haskell and covers higher order functions.

Higher order functions often use an accumulator so can be used when forming a list of elements which conform to a given rule from a larger list.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文