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

在函数式编程中,什么时候选择无点风格和以数据为中心的风格比较合适?

  •  3
  • customcommander  · 技术社区  · 7 年前

    如果重要的话,这是关于JavaScript中的函数编程,在我的示例中,我将使用Ramda。

    这两个函数的作用完全相同: 获取一个列表并返回一个新列表,其中所有字符串都已修剪。

    // data-centric style
    const trimList = list => R.map(R.trim, list);
    
    // point-free style
    const trimList = R.map(R.trim);
    

    var opts = {a: 'foo', b: 'bar', c: 'baz'}; 
    var list = ['foo', 'foo', 'bar', 'foo', 'baz', 'bar'];
    
    myFilter(opts, 'a', list); //=> ["foo", "foo", "foo"]
    myFilter(opts, 'b', list); //=> ["bar", "bar"]
    
    // data-centric style
    const myFilter = (opts, key, list) => {
      var predicate = R.equals(opts[key]);
      return R.filter(predicate, list);
    };
    
    // point-free style
    const myFilter = R.converge(
      R.filter, [
        R.converge(
          R.compose(R.equals, R.prop), [
            R.nthArg(1),
            R.nthArg(0)]),
        R.nthArg(2)]);
    

    除了可读性和个人品味之外,是否有可靠的证据表明,在某些情况下,一种风格比另一种更适合?

    3 回复  |  直到 7 年前
        1
  •  6
  •   user10675354 user10675354    7 年前

    学术术语是 eta转换 . 当你有一个带有冗余lambda抽象的函数,比如

    const trim = s => s.trim();
    const map = f => xs => xs.map(x => f(x));
    
    const trimList = xs => map(trim) (xs); // lambda redundancy
    

    const trimList = map(trim);
    

    当你使用 eta降低

    实际上,至少有两个理由可以使用 eta抽象 (与之相反 eta降低 )在Javascript中:

    • map = f => xs => xs.map(x => f(x))
    • recur = f => x => f(recur(f)) (x)
        2
  •  6
  •   Scott Sauyet    7 年前

    但其中大部分与比这种句法决定更基本的元素有关:等式推理意味着我们可以在更坚实的基础上构建自己的抽象。因此,纯度和不变性至关重要;无积分只是一种享受。

    也就是说,它通常更简单。这很重要。更简单的代码更容易阅读,更容易修改。请注意,我区分 易于理解的 容易的 --这部经典作品所表达的区别 talk by Rich Hickey

    通过不定义中间变量,甚至不指定可以推断的参数,我们可以显著提高简单性。

    很难说:

    const foo = (arg) => {
      const qux = baz(arg)
      return bar(qux)
    }
    

    const foo = (arg) => bar(baz(arg))
    

    比这更简单:

    const foo = compose(bar, baz)
    

    这是因为这三个概念都涉及到:

    • 函数声明

    第二条还补充说:

    • 参数定义
    • 功能体
    • 函数应用

    第一个版本有:

    • 参数定义
    • 功能体
    • 局部变量定义
    • 局部变量赋值
    • return 陈述

    而第三个只增加了

    • 功能组合

    如果simpler意味着更少的概念纠缠在一起,那么无点版本就更简单了,即使有些人对它不太熟悉。


    最后,这在很大程度上取决于可读性。你花在阅读自己的代码上的时间比写代码的时间要多。其他人花多少钱 很多 有更多的时间阅读。如果您编写的代码简单易读,那么您就为每个人带来了更好的体验。因此,如果无点代码更具可读性,请使用它。

    但是,不要为了证明一个观点而把每一点都去掉。你很容易陷入这样的陷阱,试图让每件事都无所谓,仅仅因为你能做到。我们已经知道这是可能的;我们不需要看到血淋淋的细节。

        3
  •  1
  •   SC K    7 年前

    有一些很好的答案,在我看来,混合这两种风格是最好的选择。

    最后一个无点样式示例有点混乱,您可以使其不那么混乱:

    const myFilter = converge(
      filter,
      [compose(equals , flip(prop)) , nthArg(2)]
     )
    
    推荐文章