Loops 函数式中的For循环

Loops 函数式中的For循环,loops,for-loop,functional-programming,Loops,For Loop,Functional Programming,在函数式编程书籍中,一个反复出现的主题是FP意味着告诉计算机做什么,而不是如何做。给出了一些代码示例,如: val newNumbers = oldNumbers.map(_ * 2) 解释是,我们不使用传统的for循环,而是使用映射 但是不是map内部实现for循环。所以我们刚刚将for循环从代码转移到了其他部分。 那么,它究竟是如何使FP优于命令式的呢?去容忍和命令式 让我们继续使用您的示例,并将所有值加倍 命令 计算机,从第一个索引到最后一个索引,每次索引小于数组长度时,都增加当前索引

在函数式编程书籍中,一个反复出现的主题是FP意味着告诉计算机做什么,而不是如何做。给出了一些代码示例,如:

val newNumbers = oldNumbers.map(_ * 2)
解释是,我们不使用传统的
for循环
,而是使用
映射

但是不是
map
内部实现for循环。所以我们刚刚将for循环从代码转移到了其他部分。 那么,它究竟是如何使FP优于命令式的呢?

去容忍和命令式 让我们继续使用您的示例,并将所有值加倍

  • 命令
    • 计算机,从第一个索引到最后一个索引,每次索引小于数组长度时,都增加当前索引。如果当前索引较小,请将当前索引加倍并将其添加到新数组中,否则将返回新数组。 function double(arr) { newArray = [] for(index = 0; index < arr.length; i++) { newArr.push(arr[index] * 2) } return newArray } 双功能(arr){ newArray=[] 对于(索引=0;索引
  • 消瘦的
    • 计算机,请给我一个新的数组,所有的值都要加倍 arr.map(double) arr.map(双精度)
  • 实施 map函数隐藏并抽象实现,而不是一次又一次地重新实现它。作为一个map用户,我对函数是通过循环、递归还是完全不同的编程语言实现的不感兴趣

    主要的一点是,它必须做好自己的工作

    const map=fn=>xs=>{
    让newArray=[]
    for(设i=0;i
    x*2
    console.log(
    地图(双)([1,2,3,4])
    )
    但是映射函数不是在内部实现for循环吗

    通常不会(至少在函数式语言中)。在一些不太多范式的函数式语言中,for循环甚至不存在

    map
    的一个常见实现是:

    map(f, []) = []
    map(f, x::xs) = f(x) :: map(f, xs)
    
    那么,它究竟是如何使FP优于命令式风格的呢

    因为,即使使用for循环实现了
    map
    ,使用
    map
    的代码也可以在没有任何可变变量或数据结构的情况下编写


    因此,不必将列表放入可变变量并从循环中重新分配它,或者更糟糕的是,使用可变列表从for循环中插入元素,您可以使用一个包含不可变列表的不可变变量,无论幕后发生了什么,它都可以正常工作。

    这对于注释来说太长了,也许这确实是一个答案

    我将借用Apocalisp的评论:

    for循环在内部由跳转指令(goto)实现。但你不会因为循环是如何实现的,就放弃循环,到处使用gotos

    出于以下原因,我们不再使用GOTO(或longjmp/setjmp):

  • 跟随控制流太难了
  • 它们容易出错,很难正确使用
  • 相反,我们使用
    表示
    表示
    ,等等(是的,
    映射
    )。我们的意思更清楚,更难(尽管我仍然认为太容易)在这些构造上犯错误


    它如何证明FP得分高于命令式?我可以很好地将命令式实现放在一个编码良好的方法中,并在任何需要的地方调用它

    是的,你可以这样做。您不应该这样做,因为您应该使用经过良好测试和流行的第三方库或内置语言结构,而不是自己滚动所有内容。即使您是一名出色的开发人员,您也无法与流行编译器的作者(他们往往是该领域的顶尖人物)或数百名在流行的开源库中修复边缘案例bug的贡献者竞争


    我可以强制实现一个方法,该方法返回一个新列表而不修改输入列表,并在需要时调用该方法

    对。同样,你可以做到。但请考虑以下内容:

    function doubleEvery(arr) {
      let i;
      let l = arr.length;
      let result = [];
      for (i=0; i<length; ++i) {
        result.push(arr[i] * 2);
      }
    
      return result;
    }
    
    函数双精度(arr){
    让我;
    设l=arr.length;
    让结果=[];
    
    for(i=0;iIt隐藏并抽象实现,而不是总是重新实现itA
    for
    循环由
    跳转
    指令(goto)在内部实现。但您不会因为循环是如何实现的,就放弃循环,到处使用gotos。@Apocalisp这应该是公认的注释:D@Apocalisp,您能详细说明一下您的答案吗?有时是一个循环。有时是将不同的部分分配给不同的CPU,甚至是不同的群集节点。部分价值在于此时,不再需要预先做出决定;可以透明地针对节点/集群成员等进行分发。将
    for
    循环与
    map
    进行比较有点不公平,因为后者表达性较差。您不能在所有情况下都隐藏递归。我不理解您的不公平是什么意思。另外y我的目标不是隐藏递归,而是为用户创建相同的函数调用-但感谢您的提示!
    map
    只涵盖了可以用
    for
    循环表示的迭代算法的子集。您必须使用其他迭代高阶函数,甚至诉诸显式递归来覆盖它们。all迭代高阶函数用于替换显式递归——这就是我对隐藏递归的意思。它如何证明FP比命令式更优秀?我可以很好地将命令式实现放在一个编码良好的方法中,并在我需要的任何地方调用它。@Mandroid选择一个范例并不是关于哪个“更好”。这是关于您喜欢的推理方式。如果您喜欢数学,请使用FP。如果您喜欢经典软件工程,请使用OOP。如果您对这两个领域都感到满意,请同时使用。我可以强制实现一个方法,该方法返回一个新列表,而无需修改输入列表,然后调用
    function doubleEvery(arr) { // iterating is coupled to doubling
      let i; // unitialized variable
      let l = arr.length;
    
      // Have to allocate result object, no opportunity for compiler
      // to optimize it away or use structural sharing
      let result = [];
      for (i=0; i<length; ++i) { // possible off-by-one error
    
        // Here if it's a more complex operation than simply doubling
        // we can't just pull this part out to test it independently
        result.push(arr[i] * 2);
      }
    
      return result;
    }