【问题标题】:Parametrizing and reduce in Functional Programming函数式编程中的参数化和归约
【发布时间】:2018-01-30 03:43:20
【问题描述】:

我正在浏览一篇名为 Why functional programming matters 的论文。它首先介绍如何计算列表中数字的总和。然后,该公式强调可以通过将列表中的数字与列表其余部分的 sum 函数相加来以递归方式计算该总和,并且将此过程称为reduce,然后写入reduce的定义可以通过参数化sum的定义来获得:

(reduce f x)  nil = x
(reduce f x) (cons a l)  =  f a ((reduce f x ) l)

我想知道的是,我们在函数式编程的程序中实际上是这样写的还是这只是一个概念的说明?

同样是我的问题,它说明了另一个函数doubleaddcons,可以写成如下:

doubleall = reduce doubleandcons nil 

在哪里doubleandcons num list = cons (2*num) list

它实际上是在程序中这样写的还是只是一个概念的说明?

【问题讨论】:

  • 对于旨在纯函数式或几乎完全函数式的语言(OCAML、Haskell 等),编译器可以很好地优化像 reduce 这样的原语的函数式表达式。在那些,是的,你做这样的程序。但是大多数具有功能特性的命令式语言(Python、Ruby、Java 8、Scala 等)都不会打扰。他们会给你一个库reduce 实现,它在底层使用迭代。
  • 不清楚是什么让你困惑,但如果是语法,引言说“我们正在遵循特纳的语言米兰达”。 Miranda 是 Haskell 的先驱。语法非常相似。如果您想知道是否有人真正使用像 reduce 这样的高阶函数,答案是“一直”。
  • 是的,这个特殊的例子说明了参数化模式的概念——在这种情况下,循环(或递归)在一个列表上。我们做了比这更奇怪的抽象:-)

标签: functional-programming


【解决方案1】:

我无法与整个函数式编程世界交谈,但我可以提供一个示例,说明如何用 JavaScript 等在某种程度上支持函数式编程的通用语言编写这样的东西。

const nums = [1, 2, 3, 4, 5];
const sum = nums.reduce((x, xs) => x + xs);
console.log(sum);

但不确定这是否能准确回答您的问题。我假设您正在寻找 reduce 功能概念的真实用例。

【讨论】:

  • 即使在 JS 中,我们也希望空数组总和为 0
  • 对我来说,这个问题听起来更像是关于reduce(以及类似的东西)是如何实现的。
【解决方案2】:

本书使用 Haskell 的前身 Miranda,因此示例是用该语言编写的功能代码。我不太了解 Miranda,但在 Haskell 中,您可以用几种不同的方式编写相同的代码,这对于典型的 Algol 程序员来说可能更容易阅读。

在Algol中,第一种风格的写作方式被称为多方法,最近很流行。例如在 Perl6 中:

multi factorial(0)      { 1 }
multi factorial(Int $x) { $x * factorial($x - 1) }

弹出的中间地带是 match,它是 switch-case 和解构的组合:

(define (factorial n)
  (match n
    [0 1]
    [n (* n (factorial (- n 1)))]))

在这里你看到它与用 JS 编写它的更经典的方式非常相似:

function factorial (n) {
  return n === 0 ? 
         1 : 
         n * factorial(n - 1);
}

以上所有只是相同的语法差异。它们的含义相同,并且支持所有这些方式的编译器可能会编译为相同的目标代码。

对于doubleall 示例,它显示了Miranda 的特征,例如currying。如果您应用的参数太少,则结果是一个接受剩余参数的函数。

reduce doubleandcons nil [1, 2, 3] 
==> [2, 4, 6]

reduce doubleandcons nil
==> function of arity 1

(reduce doubleandcons nil) [1, 2, 3]
==> [2, 4, 6] 

虽然doubleandcons 非常简单:

doubleandcons num list = cons (2*num) list

作为一个 JS 箭头函数会是

const doubleandcons = (num, list) => cons(2 * num, list)

在 Miranda(和 Haskell)中,您使用括号来更改默认关联。例如。如果没有它们在 (2*num) cons 周围,它们会被作为单独的参数给出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-06
    • 2018-08-19
    • 1970-01-01
    • 2015-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-12
    相关资源
    最近更新 更多