柯里化是如何工作的?

2024-01-26

总的来说,我对 Haskell 和 FP 很陌生。我读过许多描述柯里化是什么的文章,但我还没有找到它实际工作原理的解释。

这是一个函数:(+) :: a -> (a -> a)如果我做(+) 4 7,该函数取4并返回一个函数,该函数接受7并返回11。但会发生什么4?第一个函数的作用是什么4?什么是(a -> a)做与7?

当我考虑一个更复杂的函数时,事情变得更加混乱:

max' :: Int -> (Int -> Int)
max' m n | m > n = m
         | otherwise = n

什么是(Int -> Int)比较其参数?它只需要一个参数,但需要两个参数才能完成m > n.


理解高阶函数

Haskell 作为一种函数式语言,支持高阶函数 (HOF)。在数学中,HOF 被称为泛函 http://en.wikipedia.org/wiki/Functional_%28mathematics%29,但你不需要任何数学知识就能理解它们。在通常的命令式编程中,例如在 Java 中,函数可以接受值(例如整数和字符串),对它们执行某些操作,然后返回某种其他类型的值。

但是,如果函数本身与值没有什么不同,并且您可以接受函数作为参数或从另一个函数返回它,该怎么办?f a b c = a + b - c是一个无聊的函数,它求和a and b然后减去c。但如果我们可以的话,这个函数可能会更有趣概括如果我们有时想要求和怎么办a and b,但有时会相乘?或者除以c而不是减去?

记住,(+)只是一个返回数字的 2 个数字的函数,没有什么特别的,因此任何返回数字的 2 个数字的函数都可以代替它。写作g a b c = a * b - c, h a b c = a + b / c等等对我们来说并不合适,我们需要一个通用的解决方案,毕竟我们是程序员! Haskell 中是如何完成的:

let f g h a b c = a `g` b `h` c in f (*) (/) 2 3 4 -- returns 1.5

你也可以返回函数。下面我们创建一个函数,它接受一个函数和一个参数并返回另一个函数,该函数接受一个参数并返回一个结果。

let g f n = (\m -> m `f` n); f = g (+) 2 in f 10 -- returns 12

A (\m -> m `f` n)构造是一个匿名函数 https://wiki.haskell.org/Anonymous_function共 1 个参数m适用f对此m and n。基本上,当我们打电话时g (+) 2我们创建一个只有一个参数的函数,它只会将 2 添加到它收到的任何值上。所以let f = g (+) 2 in f 10等于 12 并且let f = g (*) 5 in f 5等于 25。

(See also my explanation of HOFs https://stackoverflow.com/a/27657332/596361 using Scheme as an example.)

了解柯里化

Currying http://en.wikipedia.org/wiki/Currying是一种将多个参数的函数转换为一个具有 1 个参数的函数的技术,该函数返回一个具有 1 个参数的函数,该函数返回一个具有 1 个参数的函数...直到它返回一个值。这比听起来更容易,例如我们有一个有 2 个参数的函数,比如(+).

Now imagine that you could give only 1 argument to it, and it would return a function? You could use this function later to add this 1st argument, now encased in this new function, to something else. E.g.:

f n = (\m -> n - m)
g = f 10
g 8 -- would return 2
g 4 -- would return 6

你猜怎么着,Haskell 默认柯里化所有函数。从技术上讲,Haskell 中不存在多个参数的函数,只有一个参数的函数,其中一些可能会返回一个参数的新函数。

从类型上就可以看出。写:t (++)在解释器中,其中(++)是一个将2个字符串连接在一起的函数,它将返回(++) :: [a] -> [a] -> [a]。类型不是[a],[a] -> [a], but [a] -> [a] -> [a], 意思是(++)接受一个列表并返回一个类型的函数[a] -> [a]。这个新函数可以接受另一个列表,它最终将返回一个新类型的列表[a].

这就是为什么 Haskell 中的函数应用语法没有括号和逗号,比较 Haskell 的f a b c使用Python或Javaf(a, b, c)。这不是什么奇怪的审美决定,在 Haskell 函数应用程序中从左到右,所以f a b c实际上是(((f a) b) c),这完全有道理,一旦你知道了f默认情况下是柯里化的。

然而,在类型中,关联是从右到左的,所以[a] -> [a] -> [a]相当于[a] -> ([a] -> [a])。它们在 Haskell 中是同一件事,Haskell 对待它们的方式完全相同。这是有道理的,因为当您仅应用一个参数时,您会返回一个类型的函数[a] -> [a].

另一方面,检查类型map http://hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html#v:map: (a -> b) -> [a] -> [b],它接收一个函数作为其第一个参数,这就是它有括号的原因。

要真正确定柯里化的概念,请尝试在解释器中找到以下表达式的类型:

(+)
(+) 2
(+) 2 3
map
map (\x -> head x)
map (\x -> head x) ["conscience", "do", "cost"]
map head
map head ["conscience", "do", "cost"]

部分应用和部分

现在您已经了解了 HOF 和柯里化,Haskell 为您提供了一些语法来使代码更短。当您调用带有 1 个或多个参数的函数以返回仍接受参数的函数时,它被称为部分应用 https://wiki.haskell.org/Partial_application.

您已经了解,您可以只部分应用函数,而不是创建匿名函数,因此不必编写(\x -> replicate 3 x)你可以写(replicate 3)。但如果你想有一个分歧怎么办(/)运算符而不是replicate?对于中缀函数,Haskell 允许您使用任一参数部分应用它。

这就是所谓的sections https://wiki.haskell.org/Section_of_an_infix_operator: (2/)相当于(\x -> 2 / x) and (/2)相当于(\x -> x / 2)。使用反引号,您可以获取任何二元函数的一部分:(2`elem`)相当于(\xs -> 2 `elem` xs).

但请记住,在 Haskell 中默认情况下任何函数都会被柯里化,因此总是接受一个参数,因此节实际上可以与任何函数一起使用:(+^)是一些奇怪的函数,它对 4 个参数求和,然后let (+^) a b c d = a + b + c in (2+^) 3 4 5返回 14。

作文

其他编写简洁灵活代码的方便工具是作品 https://wiki.haskell.org/Function_composition and 应用运营商 https://wiki.haskell.org/%24。组合运算符(.)将功能链接在一起。应用运营商($)只是将左侧的函数应用于右侧的参数,所以f $ x相当于f x。然而($)具有所有运算符中最低的优先级,因此我们可以使用它来去掉括号:f (g x y)相当于f $ g x y.

当我们需要将多个函数应用于同一个参数时,它也很有帮助:map ($2) [(2+), (10-), (20/)]会产生[4,8,10]. (f . g . h) (x + y + z), f (g (h (x + y + z))), f $ g $ h $ x + y + z and f . g . h $ x + y + z是等价的,但是(.) and ($)是不同的东西,所以请阅读哈斯克尔: 之间的区别。 (点)和 $(美元符号) https://stackoverflow.com/q/940382/596361 and 学习 Haskell 的部分内容 http://learnyouahaskell.com/higher-order-functions#function-application了解其中的差异。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

柯里化是如何工作的? 的相关文章

随机推荐

  • SQL 查询具有自定义文件扩展名的平面文件时出错

    我正在寻找一种查询平面文件的方法custom直接通过 SQL 扩展文件 通常使用类似的东西 SELECT FROM Text DATABASE C Flatfiles S7121070 ppis csv 它与 txt csv 和 tab 配
  • 使用 PEG.js 解析缩进级别

    我基本上有同样的问题Python 风格缩进的 PEG https stackoverflow com questions 4205442 peg for python style indentation 但我想得到更多关于这个答案 http
  • 如何检查变量是否在 Octave 中定义?

    当编写加载数据的脚本时 每次等待它加载都是浪费时间 如何检查变量是否已定义 您可以使用existOctave 中的函数来完成这项工作 它可用于检查给定名称作为变量 内置函数 文件或目录是否存在 在您的情况下 要检查变量是否存在 您可以使用如
  • C ++在for循环中初始化2个不同的迭代器[重复]

    这个问题在这里已经有答案了 可能的重复 我可以在 for 循环的初始化中声明不同类型的变量吗 https stackoverflow com questions 8644707 can i declare variables of diff
  • 如何覆盖 javascript 本机对象的内置方法

    假设我们有窗口对象的警报方法 我想用漂亮的警报框来增强它 另外 我想保存现有的警报方法 以便我们可以在应用程序结束后切换回来 类似这样的东西 但它在 Firefox 控制台中抛出错误 window prototype alert funct
  • pip 可以与 Visual Studio 中的 Python 工具一起使用吗?

    我正在与一些同学合作构建一个 python 应用程序 并希望使用 Visual Studio 智能感知的 训练轮 他们在 mac 和 linux 上使用 python 所以理想情况下我们的源代码控制存储库将仅包含 py我们编写的源文件 以及
  • 在 Tensorflow 中实现暹罗网络

    我想实现一个连体卷积神经网络 其中两个图像在卷积层中共享权重 然后在通过全连接层之前进行连接 我尝试过一种实现 但它似乎是一个 黑客 解决方案 特别是 我已将张量上的操作定义为简单的 Python 函数 并且我不确定这是否允许 这是我尝试过
  • 有没有可靠的方法从 postgres 间隔中提取年份?

    在 Postgres 中 如果我执行以下操作 select now created at from my table 我得到这样的结果 854 days 12 04 50 29658 然而 如果我这样做 select age now cre
  • 我什么时候需要下载.Net core运行时?

    From this https dotnet microsoft com download sdk链接 我们可以下载Core SDK和Runtime 据我了解 SDK 包含运行时 但在什么情况下我应该下载 Net Core Runtime
  • 如何使用 :: 让 vimomnicompletion 支持 php 类方法

    使用 vim php ctags 我可以获得相当好的 php 自动完成功能 但有一个部分确实让我困惑 让 vim 自动完成类方法 这是一个例子 完整的方法是 CVarDumper dumpAsString 如果我输入以下内容 我希望它完成
  • 这个周末用 php 吗?

    我想用 php 度过这个周末 我通过代码获取当前日期 start date date Y m d H i s time 如何根据当前日期获取当前周末 谢谢你 PHP 斯特托时间 http fr php net strtotime函数很神奇
  • 无法通过java客户端获取Hadoop作业信息

    我使用 Hadoop 1 2 1 并尝试通过 java 客户端打印作业详细信息 但它没有打印任何内容 这是我的 java 代码 Configuration configuration new Configuration configurat
  • 为服务指定 SEND(共享)意图过滤器

    我正在尝试过滤和处理意图android intent action SEND我的其中一项行动Services 我在我的AndroidManifest xml
  • TensorFlow 形状 (?,) 是什么意思?

    我得到的 TensorFlow 张量的形状为 这个答案 https stackoverflow com a 40953146 5353461说 意味着维度在图中并不固定 并且在运行调用之间可能会有所不同 什么是 意思是与尾随逗号连用吗 文档
  • 在 MATLAB 中预分配内存 à la std::vector::reserve(n)

    So reserve当您大致了解尺寸要求时 此功能非常有用 有谁知道在 MATLAB 中预分配数组的类似方法吗 我对像下面这样的黑客 但有效 方法并不真正感兴趣 x zeros 1000 1 for i 1 10000 if i gt nu
  • R 将文本文件导入为数据框列表

    我有一个很长的txt文件 组织如下 RANGE P1 H1 P3 H4 P10 H72 P14 H76 RANGE P1 H1 P10 H8 P11 H8 我想在 R 中阅读它 创建一个数据帧列表 其中 RANGE 和 界定每一项的内容 因
  • 使用元标记的 IE 10 兼容模式

    基本上我有这个网站 在所有版本的 IE 的兼容模式 IE7 下都运行得很好 我为此在 IIS 中使用了元标记 现在的问题是 IE10 其中文档模式已更改为 IE 标准 但浏览器模式并未更改为兼容 您能指导我找到解决方案吗 元标记会为您更改文
  • 多播委托 - 多个类从 iOS 中的 UI 控件接收通知

    据我了解 UITextField 等 UI 控件通过其委托通知客户端交互 事件 该委托被定义为支持所需协议的类 我经常发现自己想要在多个类中接收 UI 事件的通知 因此希望支持多播 例如 为一个 UI 控件指定多个委托 我非常确定没有任何
  • 创建基于编译器的“字典”的非静态版本,其中键是类型

    有一个非常简单的技巧可以创建一个类似字典的结构 其中键是类型 该结构就像一个Dictionary
  • 柯里化是如何工作的?

    总的来说 我对 Haskell 和 FP 很陌生 我读过许多描述柯里化是什么的文章 但我还没有找到它实际工作原理的解释 这是一个函数 a gt a gt a 如果我做 4 7 该函数取4并返回一个函数 该函数接受7并返回11 但会发生什么4