我正在使用scala学习函数式编程。一般来说,我注意到for循环在功能程序中没有太多使用,而是使用map。
问题
在性能,可读性等方面使用map for for循环有什么好处?
使用循环实现地图功能的目的是什么?
计划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都做同样的事情。
答案 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,熟悉函数式编程的中级软件工程师,非常精通常规编程,并且具有并行处理方面的知识,将实现常规功能和功能。