Fortran 是纯函数式语言吗?

Is Fortran a purely functional language?

我了解某些语言会结合许多不同编程范例的元素。有人告诉我 Fortran 是函数式语言的一个例子,但是我对它是否是纯函数式语言感到有点困惑,因为它似乎主要用于数学函数,但我也读到它是也可以将面向对象编程应用于 Fortran,那么它是某种混合体吗?

“我对它是否是纯函数有点困惑,因为它似乎主要用于数学函数”

这似乎是对“纯函数”一词含义的误解,也可能是对“数学函数”一词含义的误解。

数学函数

一个mathematical function是输入和输出之间的映射:

In mathematics, a function is a relation between sets that associates to every element of a first set exactly one element of the second set.

在编程语言的上下文中,函数的输入是它的参数,输出是它的 return 值,例如像

这样的函数
def greeting(name):
    return 'Hello, ' + name

在Python中被认为是数学函数,而像

这样的函数
def print_square(x):
    print(x ** 2)
    return None

不被视为数学函数。这里要注意,计算本质上是否是数学的与是否是数学意义上的函数无关; print_square 做一些算术运算,这比 greeting 做的更像数学。但是 greeting 是一个数学函数而 print_square 不是。

greeting是一个数学函数,因为它是一个字符串集合到字符串集合的映射,这个函数可以用什么输入与什么输出相关联来描述。完整地写出映射需要无限多行,但是映射是这样的:

'Alice'   → 'Hello, Alice'
'Bob'     → 'Hello, Bob'
'Charles' → 'Hello, Charles'
'####'    → 'Hello, ####'
''        → 'Hello, '
...

第一个集合(即所有字符串的集合)中的每个元素都与第二个集合中的一个元素关联,因此greeting满足数学函数的定义。

相比之下,print_square 不是数学函数,因为它不能通过从输入到输出的映射来描述:

12 → None
4  → None
...

这些映射(从整数集到 {None} 集)没有正确定义 print_square 的作用,即它计算输入的平方和 prints it to the console .

为避免与“做数学运算的函数”混淆,最好使用计算术语“pure function”:

... a pure function is a computational analogue of a mathematical function.

因此,为了解决您关于 Fortran “似乎主要用于数学函数”的评论,Fortran 主要用于进行数学计算,但这不是“数学函数”的意思。

纯函数式编程

一种purely functional编程语言是一种所有计算都由纯函数完成的语言:

In computer science, purely functional programming usually designates a programming paradigm ... that treats all computation as the evaluation of mathematical functions. Purely functional programming may also be defined by forbidding changing-state and mutable data.

请注意,编程语言允许您编写纯函数是不够的;这个定义说所有计算 必须由纯函数完成,语言才有资格成为纯函数。

Fortran 是纯函数式语言吗?不它不是。 Fortran 中的子例程可以根据输入和输出之间的映射做 return 值以外的事情; Fortran 中的计算可以通过状态变化和可变数据来完成。一个例子就足够了:这个来自 Rosetta Code.

subroutine hs(number, length, seqArray)
  integer, intent(in)  :: number
  integer, intent(out) :: length  
  integer, optional, intent(inout) :: seqArray(:)
  integer :: n
 
  n = number
  length = 1
  if(present(seqArray)) seqArray(1) = n
  do while(n /= 1)
    if(mod(n,2) == 0) then
      n = n / 2
    else
      n = n * 3 + 1
    end if
    length = length + 1
    if(present(seqArray)) seqArray(length) = n
  end do
end subroutine

变量 n 显然是可变的,因为它的状态在循环中发生变化。此外,数组 seqArray 是可变的,它既是输入 又是 子例程的输出,并且子例程会更改数组的状态。所以这个子程序没有定义纯函数,它使用了“changing-state and mutable data”,这是纯函数式语言定义所禁止的

因此子例程 hs 没有定义“数学函数”,即使它执行的计算本质上是 mathematical

命令式编程

Imperative programming 是:

... a programming paradigm that uses statements that change a program's state. ... an imperative program consists of commands for the computer to perform.

上面显示的 Fortran 子例程满足这两个定义:它使用语句或命令来更改计算机执行的程序状态。所以 Fortran 是一种命令式编程语言。

请注意,与维基百科对“纯功能”的定义不同,一种语言可以是命令式的,即使它不以这种方式进行所有计算。因此,尽管 可能 在 Fortran 中编写不能通过更改程序状态来工作的纯函数,但 Fortran 是势在必行的;只是也许不是“纯粹的命令”。

“什么”与“如何”

“这意味着 Fortran 只描述 'how' 做某事而不是 'what' 它正在尝试做什么?”

人们常说命令式程序是说“如何”计算应该完成的程序,而声明式程序只说“应该做什么”。甚至维基百科 says so:

Imperative programming focuses on describing how a program operates.

The term is often used in contrast to declarative programming, which focuses on what the program should accomplish without specifying how the program should achieve the result.

那么 Fortran 代码是否描述了程序应该“如何”做某事,或者它应该完成“什么”?

n = n / 2这样的语句是告诉计算机做某事的命令;在计算机执行命令之前有一个程序状态,之后有一个不同的程序状态。语句 n = n / 2 还告诉计算机“如何”执行命令:将 n 的当前值除以 2,并将结果存储在变量 n 中。所以根据“如何”/“什么”的区别,Fortran 是命令式的,而不是声明式的。

但您可能会争辩说:n = n / 2 说的是我们想要实现的“什么”;一个新的程序状态,其中 n 保留旧状态的值,除以 2。你是对的,它确实这么说,至少对于阅读它的人来说是这样。

这表明“如何”/“什么”的区别过于模糊,无法用作决定语言是命令式还是声明式的定义。它的意思是作为这些范式之间的松散描述的对比,而不是作为任一范式的定义。要确定一种语言是命令式的还是纯函数式的,或者在任何其他范式中,您应该参考该范式的可用定义。