CLTPayne / reduce

Functional programming challenge to implement reduce() and subsequent array methods without loops or mutation
0 stars 0 forks source link

firstAttempt.js #1

Open nicc opened 5 years ago

nicc commented 5 years ago

Loving the repo and notes! I'll play along and give feedback in an issue.

I see where you're going with firstAttempt.js but as you say, this approach can't be generalised because you're manually nesting conditionals and you'll be limited by array length.

Your ideas bullet point is spot on! More of that. It'll allow you to generalise that firstValue, secondValue thing. The way you've named your callback params in the test calls is 👍

Your final solution should only be a few lines long. You're aiming for something very simple and elegant, albeit a bit mind-bending :)

CLTPayne commented 5 years ago

Have tried to address here - https://github.com/CLTPayne/reduce/blob/master/thirdAttempt.js - but have concerns that it's still not as envisaged - it's short but it's not mind-bending yet.

nicc commented 5 years ago

Interesting! I quite like what you've done. It's very nearly there. The structure is right but there are still some problems.

You're basically applying recursion to achieve a counter, and using that to loop through the array index. It's very sneaky. I can't really fault you but it's not quite what we're going for. What you've done here is kind of like a loop in sheep's clothing.

This is a big part of the problem: const newCallCount = numberOfTimesCalled + 1 ...don't maintain a counter like this. You should be able to just pass in an updated set of params every time you recur on myReduce. The conditional is indeed there to define the terminating condition like you've got it but don't use a counter to achieve this. Recur on the array itself instead of that +1 integer. Try to think of your function as eating the array one element at a time, reducing it to nothing.

It might be a good idea to try and avoid using any const keywords. Or any variable declarations at all. The whole thing can be done using only a conditional and return statements. It's not strictly necessary and may not be idiomatic JS but that'll force you into the right direction. The idea is to lean into functions and deny yourself the easy path of modifying variables.

One thing... I totally get why you've chosen to implement this as an Array prototype method but it's denying you a very useful ES6 trick. What you've done is good JavaScript but it's not really functional (you're putting a method on an object and these concepts don't exist in pure FP). Don't worry about this for now. Stick to your prototype method but let's say you are allowed to use one specific array function to compensate: slice(). I'll show you how it can be done without it at the end.

CLTPayne commented 5 years ago

"What you've done here is kind of like a loop in sheep's clothing." - I'm proud of that!!!

Have used slice() here but including this array method makes it easy! https://github.com/CLTPayne/reduce/blob/master/fourthAttempt.js

Would much rather not use the prototype pattern (just chose it so as to most closely recreate the real reduce(). And would really like to not use slice() but can't think how to replace it yet! Want another go though!

CLTPayne commented 5 years ago

!!!! https://github.com/CLTPayne/reduce/blob/master/fifthAttempt.js

nicc commented 5 years ago

YES!!! That's it. Cool hey? Would love to hear your thoughts on what it was like going through this.

There are a few very small things I'd like to show you in that code. Forgive me for just dropping an "answer" here but you've absolutely nailed the concept and I don't think it's worth sending it back for the sake of being a pedant. I think it's worth sharing for the sake of illustration, however.

const myReduce = function([head, ...tail], callback, accumulator = 0) {
    if (head === undefined) {
        return accumulator;
    } else {
        return myReduce(tail, callback, callback(accumulator, head));
    }
}

So we're only using conditionals, equality, destructuring and functions. Yet we've managed to achieve highly generalisable list computation. Yay!!

Ready for the next exercise? I'll create a new issue with instructions. No hints this time :)