为了账号安全,请及时绑定邮箱和手机立即绑定

可读性与可维护性:嵌套函数

可读性与可维护性:嵌套函数

呼啦一阵风 2023-09-14 20:18:02
考虑到长期影响(增加函数/参数的数量、其他开发人员接管等),每个选项的优缺点是什么?选项 1:无需将foo和传递bar给每个方法,但会创建难以理解的嵌套函数。const myFunction = ({foo, bar}) => {  const results = []  const function1 = () => {    return foo + bar;  }  const function2 = () => {    return foo * bar;  }  const res1 = function1();  const res2 = function2();  results.push(res1, res2);  return results;}选项 2:将参数传递给每个函数,但删除嵌套,我认为这使其更具可读性。const function1 = ({foo, bar}) => {  return foo + bar;}const function2 = ({foo, bar}) => {  return foo * bar;}const myFunction = ({foo, bar}) => {  const results = []  const res1 = function1({foo, bar});  const res2 = function2({foo, bar});  results.push(res1, res2);  return results;}我更想知道如何改进我的功能方法。谢谢你!
查看完整描述

3 回答

?
收到一只叮咚

TA贡献1821条经验 获得超4个赞

第二种方法更为惯用。事实上,第二种方法在函数式编程中有一个名字。将共享静态值作为输入(又称为环境)的函数称为reader。

// Reader e a = e -> a


// ask : Reader e e

const ask = x => x;


// pure : a -> Reader e a

const pure = x => _ => x;


// bind : Reader e a -> (a -> Reader e b) -> Reader e b

const bind = f => g => x => g(f(x))(x);


// reader : Generator (Reader e a) -> Reader e a

const reader = gen => (function next(data) {

    const { value, done } = gen.next(data);

    return done ? value : bind(value)(next);

}(undefined));


// Environment = { foo : Number, bar : Number }


// function1 : Reader Environment Number

const function1 = reader(function* () {

    const { foo, bar } = yield ask;

    return pure(foo + bar);

}());


// function2 : Reader Environment Number

const function2 = reader(function* () {

    const { foo, bar } = yield ask;

    return pure(foo * bar);

}());


// myFunction : Reader Environment (Array Number)

const myFunction = reader(function* () {

    const res1 = yield function1;

    const res2 = yield function2;

    return pure([res1, res2]);

}());


// results : Array Number

const results = myFunction({ foo: 10, bar: 20 });


console.log(results);

在上面的示例中,我们使用一元表示法定义function1function2、 和。myFunction请注意,myFunction没有明确将环境作为输入。它也没有显式地将环境传递给function1function2。所有这些“管道”都是由purebind函数处理的。我们使用一元动作访问一元上下文中的环境ask

Reader然而,当我们使用 monad 转换器将 monad 与其他 monad结合起来时,真正的优势就会出现ReaderT


编辑:如果您不想,则不必使用单子表示法。您可以按如下方式定义function1function2、 和。myFunction

// Reader e a = e -> a


// Environment = { foo : Number, bar : Number }


// function1 : Reader Environment Number

const function1 = ({ foo, bar }) => foo + bar;


// function2 : Reader Environment Number

const function2 = ({ foo, bar }) => foo * bar;


// myFunction : Reader Environment (Array Number)

const myFunction = env => {

    const res1 = function1(env);

    const res2 = function2(env);

    return [res1, res2];

};


// results : Array Number

const results = myFunction({ foo: 10, bar: 20 });


console.log(results);

缺点是现在您明确地将环境作为输入并将环境传递给子计算。不过,这可能是可以接受的。


编辑:这是另一种编写方法,不使用一元符号,但仍然使用ask、pure和bind。


// Reader e a = e -> a


// ask : Reader e e

const ask = x => x;


// pure : a -> Reader e a

const pure = x => _ => x;


// bind : Reader e a -> (a -> Reader e b) -> Reader e b

const bind = f => g => x => g(f(x))(x);


// Environment = { foo : Number, bar : Number }


// function1 : Reader Environment Number

const function1 = bind(ask)(({ foo, bar }) => pure(foo + bar));


// function2 : Reader Environment Number

const function2 = bind(ask)(({ foo, bar }) => pure(foo * bar));


// myFunction : Reader Environment (Array Number)

const myFunction =

    bind(function1)(res1 =>

        bind(function2)(res2 =>

            pure([res1, res2])));


// results : Array Number

const results = myFunction({ foo: 10, bar: 20 });


console.log(results);

请注意,使用生成器的一元表示法只是上述代码的语法糖。




查看完整回答
反对 回复 2023-09-14
?
跃然一笑

TA贡献1826条经验 获得超6个赞

您的两种方法都是正确的。这里的问题是该代码与应用程序相关的上下文,这些函数是否与它们定义/使用的范围相关?


考虑这个例子。


const Calculator = class {

    complexOperation({foo, bar}) {

      const results = []


      const res1 = this.sum({foo, bar});

      const res2 = this.dot({foo, bar});


      results.push(res1, res2);


      return results;

    }


    sum({foo, bar}) {

      return foo + bar;

    }


    dot({foo, bar}){

      return foo * bar;

    }

};


var calc = new Calculator();

calc.complexOperation({foo: 2, bar: 3})

在这个例子中,我们可以看到功能级抽象是如何与意图相关的。


永远记住牢记“降职规则”。


现在让我们改变应用意图。现在我们正在向法律机构申请,我们必须进行复杂的操作来申请一些税收。


现在 sum 和点不应该成为类的一部分,因为只会在复杂操作中使用,新开发人员不关心 function1(我将其重命名为 sum)做什么,他们不必阅读它,所以我们可以改变抽象级别。事实上,您将以包含一些步骤的方法结束。


在其他语言中,例如c#,您可以在使用后定义函数,但在javascript中则不能,因此您不能在JS中的本地函数中应用Stepdown规则。有些人颠倒了Stepdown Rule,将所有局部函数都定义在函数start中,所以他们的眼睛就跳到最后一个局部函数结束括号并开始阅读。


const BusinessTaxes = class {

    complexOperation({foo, bar}) {

      const addTax = () => {

        return foo + bar;

      }

        

      const dotTax = () => {

        return foo * bar;

      }


      // Jump your eyes here

      const results = []

            

      const res1 = addTax({foo, bar});

      const res2 = dotTax({foo, bar});


      results.push(res1, res2);


      return results;

    };

};


var businessTax= new BusinessTaxes();

businessTaxes.complexOperation({foo: 2, bar: 3})

总之,将您的代码组织成相同的抽象级别,保持其结构化并与您的决策保持一致,您的代码将具有可读性和可维护性。


查看完整回答
反对 回复 2023-09-14
?
慕的地6264312

TA贡献1817条经验 获得超6个赞

当开发人员专注于明确自己的意图时,可读性往往是一个副产品。

下一个开发人员(或未来的您)会理解您的意图吗?

恕我直言,这是您应该回答的唯一问题吗,因为它关注的是比“这看起来不错吗?”更具体的问题。

从这个角度来看,两个版本都做到了这一点。

除了两者:

  • 可以用更好的名字

  • 可以使用新的语法来使其更容易被眼睛看到

const sumproduct_pair = ({a, b}) => {

  const sum = () => a + b;

  const product = () => a * b;

  return [sum(), product()];

};

或者


const sum = ({a, b}) => a + b;

const product = ({a, b}) => a * b;

const sumproduct_pair = ({a, b}) => [sum({a, b}), product({a, b})];

然而,这两个版本都可以改进,但还是 YMMV:


在第一个版本中, 和sum都不product需要存在。它们显然不适合重复使用,而且非常简单,可以简化为最简单的表达:


const sumproduct_pair = ({a, b}) => [a+b, a*b];

在第二个版本中,如果您打算重用sum并重product用,请考虑“针对接口而不是实现进行设计”。


该函数sumproduct_pair需要一个具有属性的对象a,b但这并不意味着所有其他函数都需要具有相同的接口:


const sum = (a, b) => a + b;

const product = (a, b) => a * b;

const sumproduct_pair = ({a, b}) => [sum(a, b), product(a, b)];

虽然这看起来像是一个微不足道的更改,但它删除了一些不必要的大括号(如果您想通过减少编写来提高可读性),最重要的是允许 和 处理sum未知product数量的数字:


const sum = (...xs) => xs.reduce((ret, x) => ret + x, 0);

const product = (...xs) => xs.reduce((ret, x) => ret * x, 1);


sum(1, 2, 3);        //=> 6

sum(1, 2, 3, 4);     //=> 10

product(1, 2, 3);    //=> 6

product(1, 2, 3, 4); //=> 24


查看完整回答
反对 回复 2023-09-14
  • 3 回答
  • 0 关注
  • 68 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信