在Javascript中引入函数式编程

时间:2014-10-23 10:16:06

标签: javascript functional-programming

我试图了解功能编程。让我们假设我有这两个功能:

/**
 * Input like: [[key1, value1], [key2, value2] ...]
 * Output like: { key1: value1, key2: value2, ... }
 */
function flattenSomeArray(arr) {
  return arr.reduce(function(prev, curr) { prev[curr[0]] = curr[1]; return prev; }, {});
}

/**
 * Input like: 'a=b;c=d;...'
 * Output like: { a: 'b', c: 'd', ... }
 */
function expandSomeString(str) {
  return str.split(';').reduce(function(prev, curr) {
    var split = curr.split('=');
    prev[split[0]] = split[1];
    return prev;
  }, {});
}

我可以看到他们有一些重复的代码,例如prev[curr[0]] = curr[1]; return prev;something.reduce。 但在我的expandSomeString中,需要为额外的split添加一些mixin。我的flattenSomeArray也可以直接使用reduce,但在expandSomeString我需要先拆分字符串。

我真的不知道如何继续,因为这个功能概念对我来说是全新的。但我确信这些简单的功能有办法。

我认为the procedure I am looking for is currying,但我不知道如何将这个概念应用到我的功能中。

2 个答案:

答案 0 :(得分:1)

我真的不知道你的目标是什么,但无论如何它听起来很有趣,让我尝试重构那段代码中的所有内容。有趣!

我同意你的意见,在这些功能中有一些重复,让我们创建另一个只执行此功能的功能:

// Add key/value pair from array to object, return the object
function addKeyFromArray(obj, keyValueArray) { 
    obj[keyValueArray[0]] = keyValueArray[1]; 
    return obj; 
}

现在我们可以直接在你的第一个函数中使用它,是的!

/**
 * Input like: [[key1, value1], [key2, value2] ...]
 * Output like: { key1: value1, key2: value2, ... }
 */
function flattenSomeArray(arr) {
  return arr.reduce(addKeyFromArray, {});
}

同样在第二个!

/**
 * Input like: 'a=b;c=d;...'
 * Output like: { a: 'b', c: 'd', ... }
 */
function expandSomeString(str) {
  return str.split(';').reduce(function(prev, curr) {
    var split = curr.split('=');
    return addKeyFromArray(prev, split);
  }, {});
}

现在split变量不再有用了:

/**
 * Input like: 'a=b;c=d;...'
 * Output like: { a: 'b', c: 'd', ... }
 */
function expandSomeString(str) {
  return str.split(';').reduce(function(prev, curr) {
    return addKeyFromArray(prev, curr.split('='));
  }, {});
}

但是那仍然感觉不太好,让我们先做第一件事。我们将使用map来获取与第一个函数一起使用的相同形状的数组。一步一步:

/**
 * Input like: 'a=b;c=d;...'
 * Output like: { a: 'b', c: 'd', ... }
 */
function expandSomeString(str) {
  var arrKeyValuePairArrays = str.split(';').map(function(strPair){
      return strPair.split('=');
  });
  return arrKeyValuePairArrays.reduce(function(prev, curr) {
    return addKeyFromArray(prev, curr);
  }, {});
}

现在有了展平功能:

/**
 * Input like: 'a=b;c=d;...'
 * Output like: { a: 'b', c: 'd', ... }
 */
function expandSomeString(str) {
  var arrKeyValuePairArrays = str.split(';').map(function(strPair){
      return strPair.split('=');
  });
  return flattenSomeArray(arrKeyValuePairArrays);
}

未经测试,但我认为看起来更好。这就是你追求的目标吗?

Soooo,currying and composing!但是,让我们使用下划线,不要重新发明它:

_.partial does the currying
_.compose does... the composing

让我们定义一个新函数来创建数组

/**
 * Input like: 'a=b;c=d;...'
 * Output like: [ ['a', 'b'], ['c', 'd'], ... ]
 */
function keyValueArrayFromStr(str) {
  return str.split(';').map(function(strPair){
      return strPair.split('=');
  });
}

然后:

function functionalSplit = function(separator, str){
    return str.split(separator);
};
var splitBySemicolon = _.partial(functionalSplit, ';');
var splitByEqual = _.partial(functionalSplit, '=');
var mapToKeyValueArray = function(strArray){ return strArray.map(splitByEqual)};
var keyValueArrayFromStr = _.compose(mapToKeyValueArray, splitBySemicolon);

然后

/**
 * Input like: 'a=b;c=d;...'
 * Output like: { a: 'b', c: 'd', ... }
 */
var expandSomeString = _.compose(flattenSomeArray, keyValueArrayFromStr);

我确定你也可以废除这两个丑陋的函数mapToKeyValueArrayfunctionalSplit。可能是bindpartialcall的一些巧妙安排。

答案 1 :(得分:1)

在函数式编程中,您可以通过创建函数来添加抽象。所以你应该问自己的问题是如何创建一个函数来为我的代码的这一部分添加抽象? 函数越少知道在其范围之外发生的事情就越可重复使用。

所以:

    var pairToObject = function(obj,pair) {
        obj = obj || {};
        obj[pair[0]] = pair[1];
        return obj;
    };

    var expandStringWith = function(separator) {
        return function(str,fn) {
            return str.split(separator).reduce(fn, {});     
        };
    };

    var reduceArrayFactory = function(fn) {
        return function(arr) {
            return arr.reduce(fn, {});
        };
    };


    var expandStringWithSemicolon = expandStringWith(';');

    /**
     * Input like: [[key1, value1], [key2, value2] ...]
     * Output like: { key1: value1, key2: value2, ... }
     */
    var flattenSomeArray = reduceArrayFactory(pairToObject);

    /**
     * Input like: 'a=b;c=d;...'
     * Output like: { a: 'b', c: 'd', ... }
     */
    var expandSomeString = function (str) {
        return expandStringWithSemicolon(str,function(obj,arr) {
            return pairToObject(obj,arr.split('='));
        });
    };