【问题标题】:Clarification on reduce to create filter in javascript关于减少在 javascript 中创建过滤器的说明
【发布时间】:2016-05-07 11:12:25
【问题描述】:

我的主要学术目标是solve this problem,而且大部分时间我都做到了。此外,我想通过不了解我的代码有什么问题来确保我没有错过 reduce 的基本部分。

这就是我要解决的方法,我已经附加了我的 reduce 函数和 filter 函数。我相信我的 reduce 函数可以工作,但是我的 filter 函数并不能很好地工作,因为我无法通过我的测试用例。

1) 为什么我没有命中数组的最后一个元素?

我注意到 prev 和 curr 实际上在 reduce 函数的任何时候都不会返回 5。我的 reduce 函数中是否缺少某些内容?

myArray = [1,2,3,4,5];

function each(collection, action){
	if(Array.isArray(collection)){
		for(var i = 0; i < collection.length; i ++){
			action(collection[i]);
		}
	}
	else if(typeof(collection) === "object"){
		for (var property in collection){
			action(collection[property]);
		}
	}
}

function reduce(collection, combine, start){
	each(collection, function(element){
		if(start === undefined){
			return start = element;
		}
		else {
			return start = combine(start, element);
		}
	});
	return start;
}


function filterR(collection, predicate){
	var aR = [];
	reduce(collection, function(prev, curr){
		if(predicate(prev)){
			aR.push(prev);
		}
		if(predicate(curr)){
			aR.push(curr);
		}
		
	});
	return aR;
}


console.log(filter(myArray, function(val){return val % 5 === 0;})); //expected [5]
console.log(filter(myArray, function(val){return val <= 5;})); //expected [1,2,3,4,5]

【问题讨论】:

  • 你要分享你的each函数
  • @naomik 感谢您指出这一点!

标签: javascript arrays functional-programming


【解决方案1】:

我无法诊断您的问题,因为我不知道您的 each 程序是什么。

不管怎样,没关系;你想多了。

reduce 手动实现非常简单。

function reduce(f, start, arr) {
  if (arr.length === 0)
    return start;
  else
    return reduce(f, f(start, arr[0]), arr.slice(1));
}

// example
function add(x,y) { return x+y; }
reduce(add, 0, []);      //=> 0
reduce(add, 0, [1,2,3]); //=> 6

我看到您正在尝试支持一种没有可选起始值的 reduce。如果你的 reduce 函数被认为是 total function,那是不可能的。起始值是必需的。不要试图变得聪明。


现在使用reduce 实现filter

function filter(f, arr) {
  return reduce(function(result, x) {
    if (f(x))
      return result.concat([x]);
    else
      return result;
  }, [], arr);
}

你的例子

var myArray = [1,2,3,4,5];

filter(function(val){return val % 5 === 0;}, myArray)
//=> [5]

filter(function(val){return val <= 5;}, myArray)
//=> [1, 2, 3, 4, 5]

如果您想了解更多...

还有其他方法可以实现reduce。您可以进行的一项改进是将递归放入tail position。当 JavaScript 引擎支持尾调用消除(使用 ES6)时,这将防止 reducearr 的非常大的值而发生堆栈溢出。

function reduce(f, start, arr) {
  function loop(result, arr) {
    if (arr.length === 0)
      return result;
    else
      return loop(f(result, arr[0]), arr.slice(1));
  }
  return loop(start, arr);
}

一个黑洞出现了……

有了reducehigher-order functions,您现在就站在了lambda calculusfunctional programming 深海的海岸边。如果您感觉非常热心,可以探索curryingcombinators 如何更大幅度地更改此定义。

以下代码是用 ES6 编写的

// U-combinator
const U = f=> f(f);

// Y-combinator
const Y = U (h=> f=> f (x=> h (h) (f) (x)));

// reduce
const reduce = f=> Y(h=> start=> arr=>
  arr.length === 0
    ? start
    : h (f (start) (arr[0])) (arr.slice(1)));


// filter
const filter = f=>
  reduce(result=> x=>
    f(x) ? result.concat([x]) : result) ([]);

// your examples again...
var myArray = [1,2,3,4,5];
filter (x=> x % 5 === 0) (myArray) //=> [5]
filter (x=> x <= 5) (myArray)      //=> [1, 2, 3, 4, 5]

【讨论】:

    【解决方案2】:

    为什么我没有命中数组的最后一个元素?

    我不知道。这可能是您的每个功能中的错误,此处未发布。但由于你的函数中还有一些缺陷,我将添加一个有效的实现。

    我注意到 prev 和 curr 实际上在 reduce 函数的任何时候都不会返回 5。

    prev 和 curr 从不返回任何内容,它们是参数。而那个特定的函数本身,也不会返回任何东西。

    First start === undefined 没有适当的检查来确定您是否要使用第一个(当前)元素初始化结果值。 undefined 在那个地方是一个意想不到的,甚至是不需要的值,但它实际上非常好。因此,通过该检查,您的 reduce-function 可能会在集合中间重新开始。

    看看前面提到的你在过滤器中使用的函数。这个函数确实返回undefined(作为下一次迭代的值);)所以prev在你的代码中总是undefined

    那么,reduce 在您的过滤器功能中没有任何意义,至少在您使用它的方式上是这样。您将其用作each

    function reduce(collection, combine, start){
        var result = start, hasValue = arguments.length > 2;
    
        each(collection, function(element){
            if(hasValue){
                result = combine(result, element);
            }else{
                result = element;
                hasValue = true;
            }
        });
    
        if(!hasValue){
            //no start/default-value, no elements in the collection,
            //what result do you expect from this reduce-task?
            throw new Error("reduce of empty array with no initial value");     
        }
        return result;
    }
    
    function filter(collection, predicate){
        return reduce(collection, function(result, elm){
            //the functional-approach: no mutations
            return predicate(elm)?
                result.concat( [elm] ):
                result;
    
            //or a more sane way to do this in JS
            if( predicate(elm) ) result.push( elm );
            return result;
        }, []);
    }
    

    【讨论】:

      猜你喜欢
      • 2015-07-30
      • 1970-01-01
      • 1970-01-01
      • 2017-06-06
      • 2020-01-07
      • 1970-01-01
      • 2018-02-19
      • 2015-12-28
      • 2016-01-18
      相关资源
      最近更新 更多