掘金 后端 ( ) • 2024-03-22 09:49

参数传递方式

参数传递方式是编程中的一个基本概念,主要指在函数或方法调用时,参数是如何从调用者传递到被调用者的。以下是对常见的三种参数传递方式的解释:

  1. 传值 (Pass by Value)

    • 在传值方式中,实际传递的是参数值的一个副本。在函数内部对参数所做的修改不会影响到外部的原始数据。
    • 这意味着,如果你传递了一个变量,函数会创建这个变量值的一个拷贝。在函数内部对这个拷贝所做的更改不会反映到原始变量上。
  2. 传地址 (Pass by Reference)

    • 传地址方式实际上传递的是参数的内存地址,而不是其值的拷贝。因此,函数内部对参数的任何更改都会直接影响到原始数据。
    • 这通常是通过传递对象引用或指针来实现的。如果你传递了一个对象或数组(在某些语言中),函数可以通过这个引用直接修改原始数据结构。
  3. 传名 (Pass by Name)

    • 传名是一种较少见的参数传递方式,主要在一些特定的编程语言或场景中使用。在传名方式中,不是立即计算参数值,而是将参数表达式未经计算直接传递到函数中。只有在函数内部实际需要该参数的值时,才会对这个表达式进行计算
    • 这种方式在一些早期的编程语言中更为常见,例如 Algol 语言。它使得函数调用变得更为灵活,但也可能导致一些理解上的困难。

传名是个什么?

传名调用 中,实际传递的不是参数的值,也不是参数的引用(地址),而是参数的表达式本身函数的参数不是在传递时计算的,而是每次在函数内部被引用时计算。这意味着,如果函数内部多次引用该参数,参数的表达式可能会被多次计算。

以下是一个简化的例子(algol语言)来说明传名的概念:

假设我们有一个简单的函数 f 和一个参数 x,我们以传名的方式将 x 传递给 f

procedure f(x);
begin
    print(x); // 第一次调用 x
    print(x); // 第二次调用 x
end;

在调用 f 时,我们传入一个表达式,而不是一个具体的值:

y := 5;
f(y * 2); // 传入的是 y * 2 这个表达式

在这个例子中,当 f 函数被调用时,x 并没有立即被计算。相反,每次 f 中引用 x 时,表达式 y * 2 都会被重新计算。如果 y 的值在两次打印 x 之间发生了变化,那么每次打印的结果可能会不同。

这个例子用现代编程语言很难准确地重现,因为大多数现代语言不支持传名调用。传名调用在现代编程实践中已经很少见,被更易于理解和实现的传值或传引用方式所取代。在某些函数式编程语言中,类似的行为可能通过惰性求值或其他机制来实现。


传名=惰性求值?

惰性求值(Lazy Evaluation)和传名调用(Pass by Name)确实都涉及到延迟计算表达式的值,但它们应用的上下文和具体行为有所不同。下面是对这两个概念的进一步区分:

  1. 惰性求值(Lazy Evaluation)

    • 这是一种编程策略,通常在整个程序或特定环境中实施,而不仅限于函数参数传递。
    • 在惰性求值中,表达式的计算被延迟,直到其值真正需要时才进行。这意味着如果一个变量的值从未被用到,其对应的计算就永远不会发生。
    • 惰性求值广泛应用于一些函数式编程语言,如 Haskell,可以有效提高程序性能,特别是在处理大型数据结构或无限数据流时。
  2. 传名调用(Pass by Name)

    • 这是一种参数传递机制,特指在函数调用时参数如何被处理。
    • 在传名调用中,传递给函数的不是参数的实际值,也不是它的引用,而是参数表达式本身。每次在函数内部引用该参数时,该表达式就被计算一次。
    • 这意味着如果函数多次引用该参数,参数表达式也会被多次计算。这种调用方式在一些早期的编程语言中比较常见,但在现代编程语言中较少使用。

总结来说,惰性求值更多关注于延迟计算以优化整个程序的性能,而传名调用则是一种特定的函数参数传递机制。两者虽然在某些方面有相似之处,但应用场景和目的不同。