【问题标题】:Is "map" a loop?“地图”是一个循环吗?
【发布时间】:2011-03-02 11:40:25
【问题描述】:

在回答this question 时,我意识到我不确定Perl 的map 是否可以被视为循环?

一方面,它像循环一样嘎嘎/走动(O(n) 是否有效,可以很容易地用等效循环重写,并且有点符合通用定义 = “不断重复的指令序列")。

另一方面,map 通常不在 Perl 的控制结构中列出,其中循环是其中的一个子集。例如。 http://en.wikipedia.org/wiki/Perl_control_structures#Loops

所以,我正在寻找一个正式的理由来说服一方与另一方。到目前为止,前者(它是一个循环)对我来说听起来更有说服力,但我对我从未在 Perl 循环列表中看到过“map”这一事实感到困扰。

【问题讨论】:

  • 顺便说一句,这肯定是因为我查看了错误的循环列表,作为答案,我很乐意接受某种实际上包含“地图”的官方循环列表
  • "Loop" 没有精确的通用定义,所以这是严格意义上的语义:答案是map 是一个循环当且仅当你认为某人是权威的(例如 Larry Wall 或共识Perl 社区的) 说是的。
  • @j_random - 这就是我的想法......有人提供了一些“官方”措辞(例如来自 O'Reilly 的书或 perlfaq 或 perldoc),或者来自深入参与的人的回应使用 Perl 内部结构(IIRC Larry Wall 不在 / 上。但 Randal Schwartz 和几个 perl 搬运工在)
  • 我明白你的意思了——了解对单词含义的共识观点很有用,这样你就可以在不引起混淆的情况下进行交流。不幸的是,在这种特殊情况下似乎没有强烈的共识。
  • @Snake - 请参阅 Eric 的回答。特别是在 perl5 中,这是有保证的。在一般的编程语言中,不是

标签: perl map loops semantics


【解决方案1】:

map 本身通常使用某种循环来实现(通常用于遍历迭代器),但由于它是更高级别的结构,它通常不包含在较低级别的列表中级控制结构。

【讨论】:

    【解决方案2】:

    map 是从函数式编程中借用的比循环更高级别的概念。它没有说“从头到尾对每个项目调用这个函数”,而是说“对所有这些项目调用这个函数”。它可以实现为一个循环,但这不是重点——它也可以异步实现——它仍然是 map。

    此外,它本身并不是一个真正的控制结构——如果在其实现中使用循环的每个 perl 函数都列在“循环”下会怎样?仅仅因为某些东西是使用循环实现的,并不意味着它应该被认为是它自己的循环类型。

    【讨论】:

    • perl 认为 map 是一种控制结构。是LOGOP mapwhile,是一个控制分支操作
    • 考虑到map { $i++ } @a 的结果,我必须说第一个引用更适用于 Perl 的“地图”而不是第二个引用(关于“所有这些项目的引用”),不是吗?
    • @Eric - 现在这是一个强有力的论点......希望将其视为实际答案!
    • @DVK,我想你可以这么说,但我更多地指的是循环和映射之间的抽象差异。 Map 是一个函数式编程概念,表示(对这些事情执行此操作),而循环是可用于实现 map 的较低级别的控制结构。
    【解决方案3】:

    “循环”更像是一个 CS 术语,而不是特定于语言的术语。如果它具有以下特征,您可以相当有信心将其称为循环:

    • 迭代元素
    • 每次都做同样的事情
    • O(n)

    map 非常适合这些,但它不是循环,因为它是更高级别的抽象。可以说它具有循环的属性,即使它本身不是最严格、最低级别的循环。

    【讨论】:

    • 循环不一定要遍历元素......或者每次都做同样的事情......或者是 O(n) 取决于循环中的内容
    • @Carson Myers:你能举一个不迭代任何东西的非退化案例循环的例子吗?我想不出任何反例。此外,循环的整个 point 是一遍又一遍地执行一组指令,所以我也不同意你的第二点。
    • 好吧,我猜计数器控制的循环不会真正迭代任何东西,带有分支语句的循环不一定总是做同样的事情。无论如何,我并没有贬低你,我只是在吹毛求疵
    • @John:从数字 n 开始计算 Collat​​z 猜想中涉及的序列的循环不是 O(n)。您的其他条件也没有说太多......如果循环包含一个分支,它是“每次都做同样的事情”吗?你只是断言它是确定性的吗?这不是 OP 要求的“正式推理”。
    • @pOOya:据我所知,分支是循环的一部分,所以循环每次都执行相同的指令体,但是在这个体中采用不同的路径。
    【解决方案4】:

    不,从我的角度来看,这不是一个循环。

    (perl)循环的特点是它们可以从(last)或恢复(nextredo)。 map 不能:

    map { last } qw(stack overflow);  # ERROR!  Can't "last" outside a loop block
    

    错误信息表明 perl 本身并不认为评估块是一个循环块

    【讨论】:

      【解决方案5】:

      从学术角度来看,可以根据地图的定义方式为这两种情况提供案例。如果它总是按顺序迭代,那么foreach 循环可以由map 模拟,使两者等效。 map 的一些其他定义可能允许无序执行列表以提高性能(在线程甚至单独的计算机之间划分工作)。 foreach 构造也可以这样做。

      但就 Perl 5 而言,map 总是按顺序执行,使其相当于一个循环。表达式map $_*2, 1, 2, 3 的内部结构导致以下执行顺序操作码,表明map 在内部构建为类似while 的控制结构:

      OP  enter
      COP nextstate
      OP  pushmark
      SVOP const IV 1
      SVOP const IV 2
      SVOP const IV 3
      LISTOP mapstart
      LOGOP (0x2f96150) mapwhile  <-- while still has items, shift one off into $_
          PADOP gvsv GV *_            
          SVOP const IV 2             loop body
          BINOP multiply              
          goto LOGOP (0x2f96150)  <-- jump back to the top of the loop
      LISTOP leave 
      

      【讨论】:

        【解决方案6】:

        地图是higher-order function。这同样适用于 grep。图书Higher-Order Perl 详细解释了这个想法。

        很遗憾看到讨论转向了实现细节,而不是概念。

        【讨论】:

          【解决方案7】:

          这里将 map 定义为recurrence

          sub _map (&@) {
              my $f = shift;
          
              return unless @_;
          
              return $f->( local $_ = shift @_ ),
                     _map( $f, @_ );
          }
          
          my @squares = _map { $_ ** 2 } 1..100;
          

          【讨论】:

          • 但是递归只是实现循环的一种方式。并非所有循环都是迭代的。
          • 嗯,这里的“循环”实际上应该被视为一种控制结构。递归是一个数学术语。正确定义,map 是数学抽象,而不是控制结构。无论如何,这个特定的“循环”是尾调用递归的,因此是迭代的!
          • 在 Perl 5 中它不是迭代的(你需要一个辅助函数和 goto 的子版本来优化 Perl 5 中的尾递归)。您可以将所有控制结构实现为函数,这是否使它们不再是控制结构? if(cond, f1, f2) 是否比 if (cond) { f1 } else { f2 } 少一些 if 语句?如果map 响应循环控制语句并允许设置循环标签,我会说这是一个循环。
          • 抱歉耽搁了:您当然不能将 if/else 实现为函数,因为无法评估其中一个分支。这就是为什么它们被称为“关键字”(或 Lisp 中的特殊形式)。
          【解决方案8】:

          这一切都取决于你如何看待它......

          一方面,Perl 的map 可以被认为是一个循环,只是因为它是在(当前版本的)Perl 中实现的。

          不过,另一方面,我将其视为功能性 map 并选择相应地使用它,其中包括仅假设将访问列表的所有元素,但不做任何假设关于他们将被访问的顺序。除了这带来的功能纯度的程度和给map 一个存在和被使用而不是for 的理由之外,如果某些未来版本的Perl 提供map 的可并行实现,这也让我保持良好状态。 (并不是说我对这种情况有任何期望......)

          【讨论】:

          • +1 - 我喜欢这种观点(以及作为一个现实主义者:))
          【解决方案9】:

          map 函数在 Perl 中不是循环。这可以通过nextredolastmap 中的失败清楚地看出:

          perl -le '@a = map { next if $_ %2; } 1 .. 5; print for @a'
          Can't "next" outside a loop block at -e line 1.
          

          要在map 中实现所需的效果,您必须返回一个空列表:

          perl -le '@a = map { $_ %2 ? () : $_ } 1 .. 5; print for @a'
          2
          4
          

          我认为转换是像map 这样的构造更好的名称。它将一个列表转换为另一个列表。与map 类似的函数是List::Util::reduce,但它不是将一个列表转换为另一个列表,而是将一个列表转换为一个标量值。通过使用变换这个词,我们可以谈论这两个高阶函数的共同点。

          也就是说,它通过访问列表中的每个成员来工作。这意味着它的行为很像循环,并且取决于您对“循环”的定义,它可能符合条件。注意,我的定义意味着这段代码中也没有循环:

          #!/usr/bin/perl
          
          use strict;
          use warnings;
          
          my $i = 0;
          FOO:
              print "hello world!\n";
          goto FOO unless ++$i == 5;
          

          Perl 实际上在其文档中使用了define the word loop

             loop
                 A construct that performs something repeatedly, like a roller
                 coaster.
          

          根据这个定义,map 是一个循环,因为它重复执行其块;但是,它也定义了“循环控制语句”和“循环标签”:

             loop control statement
                 Any statement within the body of a loop that can make a loop
                 prematurely stop looping or skip an "iteration".  Generally you
                 shouldn't try this on roller coasters.
          
             loop label
                 A kind of key or name attached to a loop (or roller coaster) so
                 that loop control statements can talk about which loop they want to
                 control.
          

          我认为将map 称为循环是不精确的,因为next 及其亲属被定义为loop control statements,它们无法控制map

          不过,这一切都只是在玩文字游戏。将map 描述为like-a-loop 是向某人介绍它的一种非常有效的方式。甚至map 的文档也使用foreach 循环作为其示例的一部分:

                         %hash = map { get_a_key_for($_) => $_ } @array;
          
                     is just a funny way to write
          
                         %hash = ();
                         foreach (@array) {
                             $hash{get_a_key_for($_)} = $_;
                         }
          

          不过,这一切都取决于上下文。当你试图让他或她理解这个概念时,将乘法描述为重复加法是很有用的,但你不希望他或她继续那样想。你会希望他或她学习乘法规则,而不是总是转换回加法规则。

          【讨论】:

          • 是否有任何(半)官方声明需要 next/redo/last 在“官方”Perl 循环中工作?或者这纯粹是个人喜好?
          • @DVK nextredolast 被定义为循环控制语句。这意味着任何作为循环的东西都可以由它们控制。但魔鬼在这里的定义中。请参阅我添加到答案中的额外信息。
          • => 但nextredolast 也适用于裸块和子例程。这些都不是循环。
          • @Eric Storm,来自next 和朋友的文档:“请注意,块本身在语义上与执行一次的循环相同。”是什么让您认为它们与子程序一起工作?如果您使用其中一个来尝试控制子程序,则会收到类似Can't "next" outside a loop block 的错误。
          【解决方案10】:

          您的问题涉及分类问题。至少在一种解释下,询问map 是否是一个循环就像询问map 是否是“循环”的一个子集。以这种方式构架,我认为答案是否定的。虽然map 和 Loop 有很多共同点,但也有重要的区别。

          • 循环控制Chas. Owens makes a strong case Perl 循环受循环控制,如 nextlast,而 map 不是。
          • 返回值map的目的就是它的返回值;有循环,没那么多。

          我们在现实世界中总是会遇到这样的关系——彼此有很多共同点,但都不是彼此的完美子集。

           -----------------------------------------
          |Things that iterate?                     |
          |                                         |
          |      ------------------                 |
          |     |map()             |                |
          |     |                  |                |
          |     |          --------|----------      |
          |     |          |       |          |     |
          |     |          |       |          |     |
          |      ------------------           |     |
          |                |                  |     |
          |                |              Loop|     |
          |                 ------------------      |
          |                                         |
           -----------------------------------------
          

          【讨论】:

          • 如果我听到迭代器这个词,我会郁闷一整天。昨天我受够了 C++,非常感谢!
          • @DVK:你问过! map 在大多数意义上确实是一个迭代器,因为它将列表元素别名为$_
          【解决方案11】:

          我认为 map 更类似于运算符,例如乘法。您甚至可以将整数乘法视为加法循环:)。这当然不是一个循环,即使它是以这种方式愚蠢地实现的。我看地图也是这样。

          【讨论】:

          • 你说的是“减少”的概念。以与本 Q 不同的方式讨论了与地图相关的问题。
          【解决方案12】:

          FM 和 Dave Sherohman 的回答都很好,但让我添加另一种查看地图的方式。

          map 是一个保证能够准确查看结构中的每个元素一次的函数。它不是 control 结构,因为它(本身)是一个纯函数。换句话说,地图保留的不变量非常强大,比“循环”强得多。因此,如果您可以使用映射,那就太好了,因为您可以“免费”获得所有这些不变量,而如果您使用(更通用!)控制结构,则必须自己建立所有这些不变量想确保你的代码是正确的。

          这确实是许多高阶函数的美妙之处:您可以免费获得更多不变量,因此作为程序员,您可以将宝贵的思考时间用于维护与应用程序相关的不变量,而不必担心低级函数依赖于实现的问题。

          【讨论】:

            【解决方案13】:

            我认为map 符合Functor 的定义。

            【讨论】:

              【解决方案14】:

              Perl 中的映射是一个高阶函数,它将给定函数应用于数组的所有元素并返回修改后的数组。

              这是使用迭代循环还是通过递归或任何其他方式实现的不相关且未指定。

              所以地图不是循环,尽管它可以使用循环来实现。

              【讨论】:

                【解决方案15】:

                如果忽略左值,地图只会看起来像一个循环。你不能用 for 循环来做到这一点:

                print join ' ', map { $_ * $_ } (1 .. 5)
                1 4 9 16 25
                

                【讨论】:

                  猜你喜欢
                  • 2016-11-17
                  • 2019-08-15
                  • 2018-09-30
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-05-15
                  • 2019-07-13
                  相关资源
                  最近更新 更多