对于函数式编程中的vs map

时间:2015-05-23 20:10:06

标签: scala functional-programming

我正在使用scala学习函数式编程。一般来说,我注意到for循环在功能程序中没有太多使用,而是使用map。

问题

  1. 在性能,可读性等方面使用map for for循环有什么好处?

  2. 使用循环实现地图功能的目的是什么?

  3. 计划1:使用For循环

    var inputs = [].slice.call(document.querySelectorAll('.inventoryInput'));
    
    if (inputs.some(haveValueUnderZero)) {
        //error
    }
    
    function haveValueUnderZero(input) { return input.value < 0; }
    

    计划2:使用地图

    val num = 1 to 1000
    val another = 1000 to 2000
    for ( i <- num )
    {
      for ( j <- another) 
      {
        println(i,j)
      }
    }
    

    程序1和程序2都做同样的事情。

4 个答案:

答案 0 :(得分:4)

实际上答案很简单。

每当您在集合上使用循环时,它都具有语义目的。要么迭代集合的项目并打印它们。或者您希望将元素的类型转换为另一种类型(map)。或者您想要更改基数,例如计算集合元素的总和(折叠)。

当然,所有这些也可以使用for循环但是对于代码的读者来说,与一个众所周知的命名操作(例如map,iter)相比,确定循环具有哪些语义目的更有意义。 ,折叠,过滤,......

另一个方面是,for循环导致使用可变状态的黑暗面。如何在没有可变状态的for循环中总结集合的元素?你不会。相反,你需要编写一个递归函数。因此,为了更好的衡量,最好放弃早期思考for循环的习惯,享受勇敢的新功能性做事方式。

答案 1 :(得分:2)

我首先引用Scala中的编程。 “每个for表达式都可以用三个高阶函数map,flatMap和filter来表示。本节描述了转换方案,它也被Scala编译器使用。” http://www.artima.com/pins1ed/for-expressions-revisited.html#23.4

因此,您注意到for循环的原因并没有那么多,因为它们在技术上并不需要,而且您看到的任何表达式都只是语法糖,编译器将转化为某些等价物。将for表达式转换为map / flatMap / filter表达式的规则列在上面的链接中。

一般来说,在函数式编程中,没有变量的索引变量。这意味着通常会大量使用函数调用(通常以递归的形式),例如代替while或for循环的列表折叠。

对于使用列表折叠代替while / for循环的一个很好的例子,我推荐Tony Morris的“Explain List Folds to Yourself”。 https://vimeo.com/64673035

如果一个函数是尾递归的(用@tailrec表示),那么它可以被优化,以免引起在递归函数中常见的堆栈的高使用。在这种情况下,编译器可以将尾递归函数转换为“while循环等效函数”。

为了回答问题1的第二部分,在某些情况下,人们可以提出一个论证,即for表达式更清楚(尽管确实存在相反情况也是如此。)在Coursera中给出了一个这样的例子。 .org课程“Scala的功能编程”,作者:Martin Odersky博士:

for {
  i <- 1 until n
  j <- 1 until i
  if isPrime(i + j)
} yield (i, j)

可以说比

更明确
(1 until n).flatMap(i =>
  (1 until i).withFilter(j => isPrime(i + j))
    .map(j => (i, j)))

有关更多信息,请查看Martin Odersky博士在Coursera.org上的“Scala功能编程”课程。第6.5讲“For For的翻译”特别详细讨论了这一点。

此外,作为一个简短的说明,在您的示例中使用

mapper.map(x => println(x))

在这种情况下使用foreach通常更容易接受,因为你有副作用的意图。此外,还有短手

mapper.foreach(println)

对于问题2,最好使用map函数代替循环(特别是当循环中有变异时)因为map是一个函数而且可以组合。此外,一旦熟悉并习惯使用地图,就很容易推理出来。

答案 2 :(得分:1)

您提供的两个程序相同,即使输出可能表明它们是。确实for理解被编译器去掉了,但是你拥有的第一个程序实际上相当于:

val num = 1 to 1000
val another = 1000 to 2000
num.foreach(i => another.foreach(j => println(i,j)))

应该注意上面(和你的示例程序)的结果类型是Unit

对于第二个程序,由编译器确定的程序的结果类型是Seq[Unit] - 现在是Seq,它具有循环乘积的长度成员。因此,您应始终使用foreach来指示导致Unit结果的效果。

答案 3 :(得分:0)

考虑机器语言级别的情况。循环仍然是基本的。函数式编程将常规编程中实现的循环抽象化。

从本质上讲,与在常规编程或抛物线式编程中编写循环不同,在函数式编程中使用链接或管道允许编译器为用户优化代码,而map只是将函数映射为每个元素遍历列表或集合。函数式编程更方便,并且抽象了“ for”循环等的普通实现。这种便利性有局限性,特别是如果您打算使用函数式编程来实现并行处理。

根据软件工程师或开发人员的说法,可以认为编译器将更加高效,并且可以提前知道其实现的情况。IMHO,熟悉函数式编程的中级软件工程师,非常精通常规编程,并且具有并行处理方面的知识,将实现常规功能和功能。