Related
I want to achieve the same result I can get with this code:
function fibs(n) {
let fibs = []
for (let i = 0; i <= n; i++) {
if ((i <= 1)) fibs.push(i)
else fibs.push(fibs[i - 1] + fibs[i - 2])
}
return fibs
}
console.log( fibs(8) )
with a recursive function.
Obviously, when you console.log(fibs(8) it renders a list like this: [0, 1, 1, 2, 3, 5, 8, 13, 21]
My recursive function looks like this:
function fibsRec(n) {
if (n < 2) return n
return fibsRec(n - 1) + fibsRec(n - 2)
}
console.log( fibsRec(8) )
and if you console.log(fibsRec(8)) it returns 21, which is the 8th Fibonacci number, but doesn't give me the list of all the Fibonacci numbers before it. How can I get the list without a loop, just from my recursive function?
How can I get the same outcome as fibs() with fibsRec()
where it goes wrong
Let's review. If fibsRec is meant to return an array, we can first notice that return n isn't going to work. n is just a number and we need an array.
function fibsRec(n) {
if (n < 2) return n // <- problem one
return fibsRec(n - 1) + fibsRec(n - 2) // <- problem two
}
Second, if fibsRec is going to be returning arrays, we cannot simply call + for fibsRec(n - 1) and fibsRec(n - 2). Watch what happens if we were to try -
const a = [1,2,3]
const b = [4,5,6]
console.log(a + b) // 1,2,34,5,6
Maybe you're thinking that's an odd result. Really JavaScript should throw an error for such misuse of +, but instead it tries its "best" to perform the addition. To do so, it coerces each array to a string first, then combines the strings together -
const a = [1,2,3]
const b = [4,5,6]
console.log(String(a)) // 1,2,3
console.log(String(b)) // 4,5,6
console.log(a + b) // 1,2,34,5,6
behaviour-oriented design
To understand how fibsRec needs to behave, let's first define some outputs for known inputs -
f(n)
output
f(0)
[]
f(1)
[0]
f(2)
[0,1]
f(3)
[0,1,1]
f(4)
[0,1,1,2]
f(5)
[0,1,1,2,3]
f(6)
[0,1,1,2,3,5]
...
...
To fix the first problem, easy mode, change return n to return a range 0..n instead -
function fibsRec(n) {
if (n < 2) return range(0,n) // <- fix one
return fibsRec(n - 1) + fibsRec(n - 2) // ...
}
const range = (a, b) =>
a >= b
? []
: [a, ...range(a + 1, b)]
you can't + arrays, but you can fibplus them...
To fix the second problem, we need a function that can "add" fibonacci sequences (arrays) because + just isn't going to cut it. We'll call our function fibplus -
function fibsRec(n) {
if (n < 2) return range(0,n)
return fibplus(fibsRec(n - 1), fibsRec(n - 2)) // <- fix two
}
We just have to define how fibplus will add the sequences to achieve the correct result. Let's work off an example. To compute fib(6) we need to "add" fib(5) and fib(4). We could just try stacking the two sequences and adding down to get the result -
0 1 1 2 3 == fib(4)
+ 0 1 1 2 3 5 == fib(5)
------------------------------------
0 1 2 3 5 8 ~~ fib(6)
It's very close to fib(6) but notice it's off by one. Watch what happens when we prepend a 1 to the smaller number before adding -
1 -> 1 0 1 1 2 3
+ 0 1 1 2 3 5
------------------------------------
1 1 2 3 5 8 ~~ fib(6)
Now if we prepend a 0 to the sum ...
1 0 1 1 2 3
+ 0 1 1 2 3 5
------------------------------------
0 -> 0 1 1 2 3 5 8 == fib(6)
We now have fib(6)! We just need to write fibplus to implement this adding technique -
const fibplus = (a, b) =>
[0, ...zip(add, a, [1, ...b])]
const zip = (f, a, b) =>
a.map((v, i) => f(v, b[i]))
const add = (a, b) => a + b
functioning demo
Run the snippet below to verify the result in your own browser -
const fib = n =>
n < 2
? range(0, n)
: fibplus(fib(n - 1), fib(n - 2))
const range = (a, b) =>
a >= b
? []
: [a, ...range(a + 1, b)]
const fibplus = (a, b) =>
[0, ...zip(add, a, [1, ...b])]
const zip = (f, a, b) =>
a.map((v, i) => f(v, b[i]))
const add = (a, b) => a + b
console.log(String(fib(20)))
0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181
visualizing
So indeed we were able to make fibsRec work using fibplus, but by mirroring the original recursive process we inherited a lot of inefficiency as well. We can see the sheer amount of duplicated work -
#WillNess comments below and explains another way fibplus can be rewritten to save some work, but the real drawback of the approach above is the resulting exponential process. Let's see some other ways to get the result we are looking for.
other processes
I like the way you asked the question: "How can I get the same outcome?". Different procedures evolve different processes, and we are not required to create a recursive branching process. Instead a linear iterative process is more efficient and better suited for the desired output.
Note fibs returns an array, but I cast the output as a string for more digestible output -
const fibs = (n, a = 0, b = 1) =>
n <= 0
? []
: [a, ...fibs(n - 1, b, a + b)]
console.log(String(fibs(10)))
So how does it work? Recursion is a functional heritage and so using it with functional style yields the best results. This means avoiding things like mutation, variable reassignments, or other side effects. When a function is referentially transparent, its call can be replaced by its return value, without changing the meaning of our program.
fibs(6)
== fibs(6, 0, 1)
== [0, ...fibs(5, 1, 1)]
== [0, ...[1, ...fibs(4, 1, 2)]]
== [0, ...[1, ...[1, ...fibs(3, 2, 3)]]]
== [0, ...[1, ...[1, ...[2, ...fibs(2, 3, 5)]]]]
== [0, ...[1, ...[1, ...[2, ...[3, ...fibs(1, 5, 8)]]]]]
== [0, ...[1, ...[1, ...[2, ...[3, ...[5, ...fibs(0, 8, 13)]]]]]]
== [0, ...[1, ...[1, ...[2, ...[3, ...[5, ...[]]]]]]]
== [0, ...[1, ...[1, ...[2, ...[3, ...[5]]]]]]
== [0, ...[1, ...[1, ...[2, ...[3, 5]]]]]
== [0, ...[1, ...[1, ...[2, 3, 5]]]]
== [0, ...[1, ...[1, 2, 3, 5]]]
== [0, ...[1, 1, 2, 3, 5]]
== [0, 1, 1, 2, 3, 5]
wasteful intermediate arrays
You might notice that the many intermediate arrays is somewhat wasteful and the result could be accomplished with a single array. Let's make a push helper to do just that -
const push = (arr, val) =>
(arr.push(val), arr)
const fibs = (n, a = 0, b = 1, r = []) =>
n == 0
? r
: fibs(n - 1, b, a + b, push(r, a))
console.log(String(fibs(10)))
Let's see how this one works -
fibs(6)
== fibs(6, 0, 1, [])
== fibs(5, 1, 1, [0])
== fibs(4, 1, 2, [0,1])
== fibs(3, 2, 3, [0,1,1])
== fibs(2, 3, 5, [0,1,1,2])
== fibs(1, 5, 8, [0,1,1,2,3])
== fibs(0, 8, 11, [0,1,1,2,3,5])
== [0,1,1,2,3,5]
streams
Another fun way to calculate sequences of fibonacci numbers is to use streams. Streams deliver data over time as it is needed, instead of all at once. Because streams allow us to consume only as much as need, we can actually define fibs as an infinite stream. Notice it is no longer a function -
const fibs =
stream(0, _ =>
stream(1, _ =>
streamAdd(fibs, fibs.next)))
The building blocks of our streams are emptyStream and stream. To construct a non-empty stream, we give provide any value to stream and a thunk _ => ... where ... is computation of the next value, if any -
const emptyStream =
Symbol('emptyStream')
const stream = (value, next) => ({
value,
get next() { delete this.next; return this.next = next() }
})
Streams as defined here are not the same as JavaScript's built-in generators. The primary difference is they are persistent, meaning they can be replayed any number of times. JavaScript's generators have an internal "cursor" and once it advances, you can never rewind it. This is important for our fibs stream because you can see it consumes itself twice. If we used generators, advancing the generator for one operation would permanently advance it for all others.
Next we define generic stream operations. streamAdd combines two streams of numbers using addition -
const streamAdd = (s1, s2) =>
s1 === emptyStream || s2 === emptyStream
? emptyStream
: stream(s1.value + s2.value, _ => streamAdd(s1.next, s2.next))
And because fibs is infinite, we need some way to limit how much we bite off. streamTake will terminate an infinite stream after that limit is reached -
const streamTake = (s = emptyStream, n = 0) =>
s === emptyStream || n <= 0
? emptyStream
: stream(s.value, _ => streamTake(s.next, n - 1))
Finally, to fulfill the desired output, we convert the finite stream to an array -
function streamToArray(s = emptyStream) {
const r = []
while (s != emptyStream) {
r.push(s.value)
s = s.next
}
return r
}
Run the stream demo below to verify the result in your browser -
const emptyStream =
Symbol('emptyStream')
const stream = (value, next) => ({
value,
get next() { delete this.next; return this.next = next() }
})
const streamAdd = (s1, s2) =>
s1 === emptyStream || s2 === emptyStream
? emptyStream
: stream(s1.value + s2.value, _ => streamAdd(s1.next, s2.next))
const streamTake = (s = emptyStream, n = 0) =>
s === emptyStream || n <= 0
? emptyStream
: stream(s.value, _ => streamTake(s.next, n - 1))
function streamToArray(s = emptyStream) {
const r = []
while (s != emptyStream) {
r.push(s.value)
s = s.next
}
return r
}
const fibs =
stream(0, _ =>
stream(1, _ =>
streamAdd(fibs, fibs.next)))
console.log(String(streamToArray(streamTake(fibs, 20))))
0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181
I would do this like this (it's also a little bit faster because of caching):
function fibsRec(n) {
const cache = {
1: 1,
0: 1
}
rec(n)
return Object.values(cache)
function rec(n) {
if (cache[n]) return cache[n]
cache[n - 1] ??= rec(n - 1)
cache[n - 2] ??= rec(n - 2)
return cache[n - 1] + cache[n - 2]
}
}
console.log(fibsRec(8))
Of course the easy answer would be to make a wrapper function that loops and calls fibsRec(i) each time, but that's not what you're looking for.
First, you need to think about what fibsRec is doing to see why this isn't naturally as simple as it sounds. As you already know it gets the nth fibonacci number by finding the (n-1)th and (n-2)th and to get those, it keeps going further back.
But that means that to get the n-1 and n-2 numbers, you need to generate the sequence to n-1 and n-2, not only that, but when you start generating that sequence for, let's say n-1, and you have to calculate it's previous indices, then you need two more sequences, and so on and so on. It's extremely inefficient.
But the reason I'm bringing this up, is to say that we can't just create an empty array and have it push the number we'd return before returning it, because we're making so many sequences, our array is gonna contain all those results.
Look at this:
function fibArray(n) {
const output = [];
function fibsRec(n) {
if (n < 2) {
output.push(n)
return n;
}
let num = fibsRec(n - 2) + fibsRec(n - 1)
output.push(num);
return num;
}
fibsRec(n);
return output
}
console.log( fibArray(8) )
See how many times we're calculating a number on the fibonacci sequence?
We definitely can't directly use this approach. But what we can use is dynamic programming. What we'll do is, memoize (save) each fibonacci number we calculate to a dictionary, and the next time we're looking for it, instead of recursing a new sequence, we'll just get it from the dictionary directly.
That way, we're getting each fibonacci number only once. So when we calculate it, we can push it to our output array, and it'll be a clean fibonacci sequence.
function fibArray(n) {
const output = [];
const fibs = {}; // Create memo (a dictionary)
function fibsRec(n) {
if (fibs[n]) return fibs[n]; // Check memo
if (n < 2) {
fibs[n] = n;
output.push(n)
return n;
}
let num = fibsRec(n - 2) + fibsRec(n - 1) // Start with n-2 to eventually call fibsRec(0) before fibsRec(1) and push them in that order
fibs[n] = num; // Memoize
output.push(num);
return num;
}
fibsRec(n);
return output
}
console.log( fibArray(8) )
I made this function to get all possible combinations of a set that sum to a target value, its works, but it is not as organize/efficient as possible yet.
After fry my brain trying to optimize this function i ran out of ideas and a litle bit blind, so i came here to get somes adivices and ideas for what more i can do.
const combinate = (set, target) => {
const result = []
set.forEach((element) => {
let division = Math.floor(target / element)
let remainder = target % element
while (remainder !== target) {
let combinations = []
for (let index = 0; index < division; index++) {
combinations.push(element)
}
if (remainder === 0) {
result.push(combinations.sort())
break
} else if (set.includes(remainder)) {
combinations.push(remainder)
result.push(combinations.sort())
break
} else {
division--
remainder += element
}
}
})
return result
}
Here I have some examples of expected outcomes for how this function it should work.
combinate([2, 3, 5], 8) -> [[2,2,2,2],[2,3,3],[3,5]]
I think your algorithm is fine.
I personally would structure the code differently, as I'm a fan of expression-only coding. My version might look like this:
// Ex: countDown (6) //=> [6, 5, 4, 3, 2, 1, 0]
const countDown = (n) =>
n < 0 ? [] : [n, ...countDown (n - 1)]
const subsetSum = ([n, ...ns], t) =>
n == undefined
? t == 0 ? [[]] : []
: countDown (Math.floor (t / n)) .flatMap (
(k) => subsetSum (ns, t - k * n) .map (ss => [...Array (k) .fill (n), ...ss])
)
console .log (subsetSum ([2, 3, 5], 8))
.as-console-wrapper {max-height: 100% !important; top: 0}
I count down rather than up just so the results come in the same order yours did. If I counted up, they would show up as [[3, 5], [2, 3, 3], [2, 2, 2, 2]].
But this is essentially the same algorithm as yours. If it's ill-performant, then I might look at a dynamic programming version where we calculate the results for each lower total, and then for our target total, we look up the values found by subtracting each of the number in our set, and for each of those results, we add one of that number. Here's one version:
const countUp = (n) =>
(n < 1) ? [] : [... countUp (n - 1), n]
const oneMore = (i) => (s) =>
s .split ('-') .map (Number) .map ((n, j) => j == i ? n + 1 : n) .join ('-')
const collect = (ns, t) =>
countUp (t) .reduce (
(rs, v) => [
...rs,
new Set (ns .flatMap ((n, i) => ([...rs [v - n] || []]) .map (oneMore (i))))
],
[new Set([ns .map (n => 0) .join ('-')])]
)
const subsetSum = (ns, t) =>
[...collect (ns, t) [t]]
.map (s => s.split ('-') .map(Number) .flatMap ((c, i) => Array (c) .fill (ns[i])))
console .log (subsetSum ([2, 3, 5], 8))
.as-console-wrapper {max-height: 100% !important; top: 0}
The main function here, collect accepts, say [2, 3, 5] and 8, and returns something like
[
new Set (['0-0-0']), // 0
new Set ([]), // 1
new Set (['1-0-0']), // 2
new Set (['0-1-0']), // 3
new Set (['2-0-0']), // 4
new Set (['1-1-0', '0-0-1']), // 5
new Set (['3-0-0', '0-2-0']), // 6
new Set (['2-1-0', '1-0-1']), // 7
new Set (['4-0-0', '1-2-0', '0-1-1']), // 8
]
where, say '1-1-0' represents one 2 and one 3 and zero 5s, which add up to 5, or '0-1-1' represents zero 2s and one 3 and one 5, which add up to 8. In retrospect, a better string format would probably have been the JSON-stringified version of something like {2: 1, 3: 1, 5: 0} But I'll leave that as an exercise.
The values are stored as Strings in Sets to eliminate duplicates as we go. For instance, when we hit 5, we can add a 2 to '0-1-0' or a 3 to '1-0-0', both of which end up as '1-1-0'. But we only want a single copy of that result.
We use two minor helper functions. countUp for instance, turns 7 into [1, 2, 3, 4, 5, 6, 7]. oneMore handles the string to Array of numbers back to string conversion such that
oneMore (0) ('1-7-4') //=> '2-7-4'
oneMore (1) ('1-7-4') //=> '1-8-4'
oneMore (2) ('1-7-4') //=> '1-7-5'
The main function simply extracts the last value computed by collect and then for each of the Strings in the set, converts that back into a proper array.
I have not tested for performance, but there's a real chance that this will be faster than the original algorithm. If nothing else, it demonstrates a substantially different technique.
I was working on this problem to create a function using a reduce method that will get the max number in an array.
The instructor's answer is:
const numbers = [1, 2, 3, 4, 4, 5, 1, 3, 4];
const max = getMax(numbers);
console.log(max);
function getMax(array) {
if (array.length === 0) return undefined;
return array.reduce((accumulator, current) => {
return (accumulator > current) ? accumulator : current
});
I tried something like this:
return array.reduce((accumulator, current) => {
if (accumulator < current)
console.log(accumulator, current);
return accumulator = current;
});
I added console.log (accumulator, current) because I wanted to see what's going on with my code. The log shows as follows:
console.log of my code
1 2
2 3
3 4
4 5
1 3
3 4
4
Question 1. I'd like to know why my function didn't give the right output (it returned 4, not the correct output 5). Shouldn't "accumulator" stay 5 when it is assigned as 5 during the loop?
Question 2. Why do I need to return (or add return in front of) array in the function, when there is already a return below the if statement?
You didn't use { ... } after your if statement, so only the first line console.log(...) is happening when the condition is met. The accumlator = current line always happens for each iteration. You must use return when using imperative style if statement. However you can skip return when using functional style expressions, ie (accumulator, current) => accumulator < current ? current : accumulator which says "if accumulator is less than current, return current, else return accumulator".
Consider this decomposed program. When we see max as an independent function, it helps us see precisely the type of function reduce is expecting -
const max = (a = 0, b = 0) =>
a < b // if a is less than b
? b // return b
: a // otherwise return a
const getMax = (numbers = []) =>
numbers.length === 0 // if numbers.length is zero
? undefined // return undefined
: numbers.reduce(max) // otherwise return reduction
console.log(getMax([1, 2, 3, 4, 4, 5, 1, 3, 4]))
// 5
console.log(getMax([]))
// undefined
console.log(getMax())
// undefined
We can see reduce is produces the following computation -
// given
[1, 2, 3, 4, 4, 5, 1, 3, 4]
// starting with the first two
r = max(1, 2)
// then the next number
r = max(r, 3)
// then the next number
r = max(r, 4)
// then the next number
r = max(r, 4)
Or without intermediate r = ... -
max(max(max(max(max(max(max(max(1, 2), 3), 4), 4), 5), 1), 3), 4)
We could write getMax without reduce, if we wanted -
const max = (a = 0, b = 0) =>
a < b
? b
: a
const getMax = (numbers = []) =>
numbers.length === 0 // without any numbers,
? undefined // there can be no max.
: numbers.length === 1 // if we only have one,
? numbers[0] // we already know max.
: max(numbers[0], getMax(numbers.slice(1))) // else
console.log(getMax([1, 2, 3, 4, 4, 5, 1, 3, 4]))
// 5
console.log(getMax([]))
// undefined
console.log(getMax())
// undefined
Or maybe you haven't learned slice yet. You can use an array index, i, to step thru your array -
const max = (a = 0, b = 0) =>
a < b
? b
: a
const getMax = (numbers = [], i = 0) =>
numbers.length === 0 // without any numbers,
? undefined // there can be no max.
: i + 1 >= numbers.length // if the next i is not in bounds,
? numbers[i] // this is the last number
: max(numbers[i], getMax(numbers, i + 1)) // else
console.log(getMax([1, 2, 3, 4, 4, 5, 1, 3, 4]))
// 5
console.log(getMax([]))
// undefined
console.log(getMax())
// undefined
Destructuring assignment can be used as well -
const max = (a = 0, b = 0) =>
a < b
? b
: a
const getMax = ([ num, ...more ] = []) =>
more.length === 0
? num
: max(num, getMax(more))
console.log(getMax([1, 2, 3, 4, 4, 5, 1, 3, 4]))
// 5
console.log(getMax([]))
// undefined
console.log(getMax())
// undefined
This might show you how you can invent your own reduce -
const max = (a = 0, b = 0) =>
a < b
? b
: a
const reduce = (f, a = [], i = 0) =>
a.length === 0 // without any numbers,
? undefined // there can be no reduction.
: i + 1 >= a.length // if the next i is not in bounds,
? a[i] // this is the last element
: f(a[i], reduce(f, a, i + 1)) // else
const getMax = (numbers = []) =>
reduce(max, numbers) // <-- our reduce!
console.log(getMax([1, 2, 3, 4, 4, 5, 1, 3, 4]))
// 5
console.log(getMax([]))
// undefined
console.log(getMax())
// undefined
Try use Math.max method:
const numbers = [1, 2, 3, 4, 4, 5, 1, 3, 4]
numbers.reduce((acc, rec) => Math.max(acc, rec))
//5
or
function max(numbers) {
return list.reduce((acc, rec) => acc > rec ? acc : rec)
}
if you need find max value without Math.max.
I have an function that takes an array as an input.
How can I modify it to work with variable arguments as well as arrays.
For example I want arrSum(1,2,3) === arrSum([1,2,3]) to return true i.e. both should return 6
const arrSum = arr => arr.reduce((a,b) => a+b,0)
console.log(arrSum([1,2,3]))
You can use spread syntax with concat. In first case you will get array with another array inside and in second case just an array of arguments but with [].concat(...arr) you will transform that to array of arguments for both cases.
const arrSum = (...arr) => [].concat(...arr).reduce((a, b) => a + b, 0)
console.log(arrSum([1, 2, 3]))
console.log(arrSum(1, 2, 3))
console.log(arrSum([1, 2, 3], [4], 5))
You can solve this problems in many way , one way if you're using arrow function would be like this using rest operator
const arrSum = (...arr) => {
// if we have just on param which is array
if (arr.length === 1) {
return arr[0].reduce((a, b) => a + b, 0);
}
return arr.reduce((a, b) => a + b, 0);
};
console.log(arrSum(1, 2, 3)); // 6
console.log(arrSum([1, 2, 3])); // 6
in the code above we use rest operator learn more rest_parameters MDN
also we can use normal functions using the variable arguments inside functions like this
function arrSum(args) {
if (arguments.length === 1) {
return arguments[0].reduce((a, b) => a + b, 0);
} else {
return [].reduce.call(arguments, (a, b) => a + b, 0);
}
}
console.log(arrSum(1, 2, 3));
console.log(arrSum([1, 2, 3]));
learn more about
arguments Object MDN
US/docs/Web/JavaScript/Reference/Functions/arguments
so the same problem can be solved many ways choose what works best for you .
A solution example:
function arrSum( nums ) {
var n = 0;
if ( typeof( nums ) != 'object' )
return arrSum( arguments );
for ( var i = 0; i < nums.length ; i++ )
n += nums[ i ];
return n;
}
arrSum( 1, 2, 3 );
arrSum( [ 1, 2, 3 ] );
These code was posted on CodeReview two days ago:
function curry(f, self) {
return function () {
if (arguments.length == f.length) {
return f.apply(self, arguments);
}
arguments = Array.prototype.slice.call(arguments);
return curry(f.bind.apply(f, [self].concat(arguments)));
}
}
function f(a, b, c, d) {
return this + a + b + c + d;
}
document.write("f(1, 2, 3, 4) = ", curry(f, 0)(1, 2, 3, 4), "<br>");
document.write("f(1, 2, 3)(4) = ", curry(f, 0)(1, 2, 3)(4), "<br>");
document.write("f(1)(2, 3, 4) = ", curry(f, 0)(1)(2, 3, 4), "<br>");
document.write("f(1)(2)(3)(4) = ", curry(f, 0)(1)(2)(3)(4), "<br>");
What I'm not able to understand is:
There is made a new copy of f by using bind(). The parameter already provided are assigned to the copy but what is with the variable "self"?
I've tried to "sketch" what I mean:
// Second parenthesis (marked with =>): There are three of four
// expected parameter provided:
document.write("f(1, 2, 3)(4) = ", curry(f, 0) => (1, 2, 3) <= (4), "<br>");
// Makes an array-literal with "self" (== 0) as only element in it.
// Then adds the parameter already provided to these array by
// using concat(). => Results in an array [ 0, 1, 2, 3 ].
// Then makes a new copy of f with these values bind to it as parameter.
// These new instance of the function is then passed to the curry-function.
return curry(f.bind.apply(f, [self].concat(arguments)));
The copy of f should have it's four parameter. It should be executed and resulting "return 0 + 0 + 1 + 2 + 3;" and return 6.
Why isn't that the case?
Perhaps someone can answer that. I would appreciate it.
What is with the variable "self"? It is used to overwrite the this-keyword AND it has been added to the array given to the function.
No, it's not:
f.bind.apply(f, [self].concat(arguments))
≡ f.bind.apply(f, [self].concat([1, 2, 3]))
≡ f.bind.apply(f, [0, 1, 2, 3])
≡ f.bind(0, 1, 2, 3)
self/0 is bound as the this argument, 1, 2 and 3 are bound as three partially applied parameters. Nothing is duplicated here. The result is a function
function bound_f(x, ...args)
return f.call(0, 1, 2, 3, x, ...args);
}
that is then again curried and can be invoked with 4 as the argument.