代码之家  ›  专栏  ›  技术社区  ›  bdd

什么是“pass-by-name”,它是如何工作的?

  •  32
  • bdd  · 技术社区  · 15 年前

    我查了一下维基百科,在谷歌上搜索了一下,但我仍然不能完全理解pass-by-name在algol 60中的工作原理。

    8 回复  |  直到 6 年前
        1
  •  36
  •   Greg Hewgill    15 年前

    我找到了一个很好的解释 Pass-By-Name Parameter Passing . 从本质上讲,函数体在将实际参数替换为函数体后,在调用时进行解释。在这个意义上,计算方法类似于C预处理器宏。

    通过将实际参数代入函数体,函数体可以读写给定的参数。在这个意义上,评估方法类似于参照传递。不同之处在于,由于使用pass-by名称,参数是 评价的 在函数内部,一个参数,如 a[i] 取决于 i 在函数内部,而不是引用 [我] 在调用函数之前。

    上面我链接的页面有更多的例子,其中pass-by-name既有用又危险。今天,由pass-by-name实现的技术在很大程度上被其他更安全的技术(如pass-by-reference和lambda函数)所取代。

        2
  •  13
  •   Laurence Gonsalves    6 年前

    我假设你的意思是用algol 60来命名。

    按名称调用类似于按引用调用,因为您可以更改传入参数的值。它不同于按引用调用,因为参数是 在调用过程之前对其进行了计算,但改为延迟计算。也就是说,只有在实际使用参数时才对其进行评估。

    例如,假设我们有一个过程 f(x, y) 我们通过它 i i/2 哪里 最初等于 10 . 如果 f 集合 x 42 然后评估 y 它会看到价值 21 (然而,通过引用调用或值调用,仍然可以看到 5 )这是因为表达式 I/ 2 直到 Y 进行评价。

    在许多方面,这看起来像是参数的文本替换(通过重命名来避免名称冲突)。然而,在实践中,这是通过对传入表达式使用“thunk”(基本上是闭包)来实现的。

    维基百科关于 Jensen's Device 显示了一些使用按名称调用的有趣示例。以下是其中之一:

    real procedure Sum(k, l, u, ak)
         value l, u;
         integer k, l, u;
         real ak;
         comment k and ak are passed by name;
     begin
         real s;
         s := 0;
         for k := l step 1 until u do
             s := s + ak;
         Sum := s
     end;
    

    在过程中,索引变量 k 和求和项 ak 是 按名称传递。按名称调用使过程能够更改值 执行for循环期间的索引变量。按名称呼叫 也导致 阿克 每次迭代期间要重新评估的参数 循环。通常情况下, 阿克 将取决于变化(影响侧) K .

    例如,用于计算实数前100项的和的代码 数组 V[] 将是:

    Sum(i, 1, 100, V[i]).
    
        3
  •  3
  •   zython Hiery Nomus    6 年前

    对于未来的人:

    编程语言中的概念 约翰·C·米切尔也很有帮助。

    传名 . 也许是最奇怪的 回顾ALGOL60的特点是 通行证名称的使用。在 传递名称,结果为 过程调用与 形式参数被替换成 程序的主体。这条规则 用于定义过程的结果 通过复制过程调用 替换形式参数 称为algol 60复制规则。 尽管复制规则对 纯功能程序,如 以λ减少为例 微积分,与边的相互作用 对形式参数的影响是 有点奇怪。下面是一个例子 显示参考技术的程序 作为詹森的装置:通过 表达式及其包含的变量 使程序 可以使用一个参数更改 对方提到的地点:


     begin integer i;
            integer procedure sum(i, j);
                integer i, j;
                    comment parameters passed by name;
                begin integer sm; sm := 0;
                    for i := 1 step 1 until 100 do sm := sm + j;
                    sum := sm
                end;
            print(sum(i, i*10 ))
     end
    

    在这个程序中,程序 求和(i,j)将j的值与i相加 从1到100。如果你看 代码,你会意识到 程序没有意义,除非 对i的更改会导致 j值;否则,程序 只计算电话里的100*J. 这里显示的和(i,i*10),for循环 在程序主体中,求和 从1到 100。

        4
  •  1
  •   Thomas L Holaday    15 年前

    Flatlander有一个很有启发性的例子,说明它在scala中的工作方式。 here . 假设您想要实现 虽然 :

    def mywhile(condition: => Boolean)(body: => Unit): Unit =
      if (condition) {
        body
        mywhile(condition)(body)
      }
    

    我们可以这样称呼它:

    var i = 0
    mywhile (i < 10) {
      println(i)
      i += 1
    }
    

    scala不是algol 60,但它可能会发光。

        5
  •  1
  •   bogatyrjov    12 年前

    您可以以变量的符号形式传递“name”,它允许同时更新和访问变量。例如,假设您希望将int类型的变量x三倍化:

    start double(x);
    real x;
    begin
    x : = x * 3
    end;
    
        6
  •  1
  •   G K    10 年前

    算法是为数学算法而设计的。我喜欢使用求和函数作为按名称调用的示例。

    对不起,我的算法有点生疏。语法可能不对。

    .FUNCTION SUM(var,from,to,function)
    .BEGIN
      .REAL sum =0;
      .FOR var = from .TO to .DO sum = function;
      return sum;
    .END
    

    你不能用和

      Y = sum(x,1,4,sum(y,3,8,x+y));
    

    在上面,内部和(y,3,8,x+y)将生成一个未命名的函数来传递给外部和调用。变量x和y不是按值传递的,而是按名称传递的。在变量的情况下,按名称调用等价于C中的按地址引用调用。当涉及递归时,它会变得有点混乱。

    借来的人制造海藻机。他们有48位字存储器和3个标志位。标志位按algol的名称实现了cal。它是一个堆栈机器,因此当函数加载到堆栈上时,名为fag的调用将导致调用它。当表达式用作参数时,编译器将生成未命名的函数。变量是一个简单的间接引用。写入函数时会出错。

        7
  •  1
  •   Brian Tompsett - 汤莱恩 Tejas G R    8 年前

    事实上,顾名思义,不仅仅是一种历史的好奇心。您可以在Windows批处理文件(以及无数其他脚本语言)中按名称调用。知道它是如何工作的,以及如何在编程中有效地使用它,可以为解决问题打开一个整洁的解决方案。我知道它只是为以后的扩展传递字符串,但是它可以被操纵以产生与按名称调用类似的效果。

    call :assign x 1
    exit /b
    :assign
    setlocal enabledelayedexpansion
    (endlocal
    :: Argument 1 is the name of the variable
    set %1=%2
    )
    exit /b
    
        8
  •  1
  •   nhershy    7 年前

    我知道我加入俱乐部晚了,这不一定是个答案,但我确实想补充一点,可以帮助澄清一点。我一直把Algol的名字看作是一个类似的过程,当C++预处理器指令(宏,特别是)在编译时用一段代码替换一些函数/变量的名称时。pass-by名称实质上用实际参数替换形式参数的名称,然后执行。我从来没有在Algol写过,但是我听说通过的名字和C++的通过引用会得到相同的结果。