JavaScript's bind() with currying. How does these code work? - javascript

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.

Related

Curried javascript sum function.

I came across this interesting problem. Write a javascript function that returns sum of all the arguments passed to it, through multiple calls to that same function.
Here are the ways function can be called -
sum(1, 2, 3, 4);
sum(1, 2)(3, 4);
sum(1, 2)(3)(4);
sum(1, 2, 3)(4);
sum(1)(2, 3, 4);
All the calls above should work and return 10.
Here's what I have written so far, but it only works for first two function calls sum(1, 2, 3, 4) and sum(1, 2)(3, 4) and shits the bed for rest of it.
const arr = [];
function sum(...args) {
if (args.length === 4) {
return args.reduce((acc, curr) => {
return (acc = acc + curr);
}, 0);
} else {
arr.push(...args);
return function(...args) {
arr.push(...args);
return sum(...arr);
};
}
}
Someone please help me, this is driving me nuts.
Thanks!
You're pretty close. This is a perfect opportunity to use the .bind method to return a function that captures the first arguments if you haven't yet been given at least four.
So something like:
function sum(...args) {
if (args.length >= 4) {
return args.reduce((acc, curr) => {
return (acc = acc + curr);
}, 0);
} else {
// Bind the arguments you have to this function, and return it:
return sum.bind(null, ...args)
}
}
console.log(sum(1, 2, 3, 4));
console.log(sum(1, 2)(3, 4));
console.log(sum(1, 2)(3)(4));
console.log(sum(1, 2, 3)(4));
console.log(sum(1)(2, 3, 4));
Finally, I'd change the condition to check for >= 4 so that passing more than that doesn't result in a case where you'll curry forever.
Currying has a specific and defined behavior that doesn't mix well with variadic functions due to the undefined arity. However in your particular problem you specify an arity (for example, 4) so its possible to know when to return a result
const curryN = (n, f, ...xs) =>
(...ys) =>
ys.length >= n
? f (...xs, ...ys)
: curryN (n - ys.length, f, ...xs, ...ys)
const add = (...numbers) =>
numbers.reduce ((a, b) => a + b, 0)
const curryAdd =
curryN (4, add)
console.log
( curryAdd (1) (2) (3) (4) // 10
, curryAdd (1, 2) (3, 4) // 10
, curryAdd (1, 2, 3) (4) // 10
, curryAdd (1) (2, 3, 4) // 10
, curryAdd (1, 2, 3, 4) // 10
)
This is a fragile way to design your program though, and it's not even true currying, which only accepts 1 argument per application. Partial application is better because it produces a program with a much more reliable behavior
const partial = (f, ...xs) =>
(...ys) =>
f (...xs, ...ys)
const add = (...numbers) =>
numbers.reduce ((a, b) => a + b, 0)
console.log
( partial (add, 1) (2, 3, 4) // 10
, partial (add, 1, 2) (3, 4) // 10
, partial (add, 1, 2, 3) (4) // 10
)
Please read this related answer for additional insight.

JavaScript Jest Testing; Using Callback Function to Modify Elements in Array

I'm using the Jest testing library to test a function that invokes a callback that takes two parameters, element[i], and i.
Here is the function I am to test:
const each = (elements, cb) => {
for (let i = 0; i < elements.length; i++) {
cb(elements[i], i);
}
};
I know the array will only have numbers, so in the testing function I'm trying to make the callback simply add the index to each number in the array; e.g., it should change an array of [1, 2, 3, 4] to [1, 3, 5, 7].
Here is what I tried to do:
const arrayFunctions = require('./arrays');
describe('Arrays', () => {
it('each', () => {
const callBack = (elem, indx) => {
elem += indx;
}
const arr = [1, 2, 3, 4];
arrayFunctions.each(arr, callBack);
expect(arr).toEqual([1, 3, 5, 7]);
})
});
And here is the error I'm getting:
Received:
[1, 2, 3, 4]
Difference:
- Expected
+ Received
Array [
1,
+ 2,
3,
- 5,
- 7,
+ 4,
]
15 | const arr = [1, 2, 3, 4];
16 | arrayFunctions.each(arr, callBack);
> 17 | expect(arr).toEqual([1, 3, 5, 7]);
| ^
18 | })
19 | });
20 |
at Object.it (advanced-javascript/arrays.spec.js:17:21)
Why is it not modifying the array as expected? To reiterate with an even simpler example, why does this work:
const arr = [1, 2, 3, 4];
for (let i = 0; i < arr.length; i++) {
arr[i] += i;
}
console.log(arr) // [1, 3, 5, 7];
While this doesn't?
const arr = [1, 2, 3, 4];
each(arr, callBack);
console.log(arr); // [1, 2, 3, 4];
The difference is because of a subtlety of index assignment. It looks like two operators ([n] and +=) but it is actually one: [n]+=. In the end using += (add and assign) is not the same as [n] += (add and assign to value at index), so you can put spaces between the index and the assignment characters (and strangely, even parentheses, but that's just confusing), but you can't split it into separate statement lines, which is effectively what you are doing (see below).
Let's make our own array and look at how we would implement [n] (let's call it get(n)) and [n]=value (let's call that function set(n, value). For ease of readability, I'll use the new class syntax.
class FakeArray {
get(index) {
var valueAtIndex = // somehow get that value
return valueAtIndex
}
set(index, value) {
//somehow set that value
}
}
Now think about if you could implement set using get. Would this work?:
set(index, value) {
this.get(index) = value
}
It wouldn't. You would get an error: ReferenceError: Left side of assignment is not a reference. (you can change the values of references, but not the values of values). However, if you could, you wouldn't really need the set function. You could just use the get function combined with = (and also +=, -=, etc.). But you can't split them up, so they are completely different functions / operators. It's the same with [n] and [n]=. They work entirely different, they aren't combinations of the two.
Also, when you pass values to a function, you are essentially assigning them to a new variable:
var a = 1;
function foo (b) {
b = b + 1
console.log(b)
};
foo(a)
console.log(a)
is mostly equivalent to:
var a = 1;
var b = a
b = b + 1
console.log(b)
console.log(a)
This is called pass by value, and it is the only way to pass values in javascript. See this answer for more detail: What's the difference between passing by reference vs. passing by value?

Explanation of what is actually happening in ES6 Rest paramter

Please explain the following example.I am not able to understand what is actually happening here.Thank you in advance
function f (x, y, ...a) {
return (x + y) * a.length
}
f(1, 2, "hello", true, 7) === 9
Rest takes the 'rest' of the arguments and puts in them in an array which is assigned to a.
Your return statement turns into (1+2) * 3, which equals 9.
A simpler example:
[x, y, ...a] = [1, 2, 3, 4, 5, 6]
console.log(x)
// 1
console.log(y)
// 2
console.log(a)
// [ 3, 4, 5, 6 ]
If you think of arguments being passed as a simple array, then the following
function f(a,b,...c){...}
Can be converted to:
function f(args){
var a = args[0],
b = args[1],
c = args.slice(2);
//...
}
//function is getting defined here.
function f (x, y, ...a) {
return (x + y) * a.length
}
//function is getting called here.
f(1, 2, "hello", true, 7) === 9
When your function f is called, Inside the function f, your code takes x as 1, y as 2, and a as an array containing ["hello", true, 7]. So, array a has length 3.
Your result of the function is (1 + 2) * 3, which is 9.
So, your comparison of function result to 9, is true.

Why in this example in "applyAll" function , parameter after argument setted 1, not 0 ?

function sum() {
return [].reduce.call(arguments, function(a, b) {
return a + b;
});
}
function mul() {
return [].reduce.call(arguments, function(a, b) {
return a * b;
});
}
why there is a second parameter is 1? If we want shift all array or argumentsm, it`s like we need to to set start parameter 1 , not 0. Cause if we used 1 , we will lost argument with key 0.
function applyAll(func) {
return func.apply(this, [].slice.call(arguments, 1));
}
console.log( applyAll(sum, 1, 2, 3) ); // 6
console.log( applyAll(mul, 2, 3, 4) ); // 24
console.log( applyAll(Math.max, 2, -2, 3) ); // 3
console.log( applyAll(Math.min, 2, -2, 3) ); // -2
This is how used [].slice.call(context, arg).
var obj= [1,2,3];
console.log([].slice.call(obj,0));
(function() {
return [].slice.call(arguments,0)
}(1,2,3))
The first argument to .call() is the context (as in, what this will be inside the function). Second argument and over are the arguments sent into the function.
For the function applyAll, the first argument is always func.
So in your code example applyAll(sum, 1, 2, 3), the arguments is [sum, 1, 2, 3]. What you are supposed to do is passing 1, 2, 3 as arguments to sum. That is why you need to get a sub array from index = 1 from the original arguments which is [sum, 1, 2, 3].
Answer to your comments:
I thought you should have the clue:
[sum, 1, 2, 3].slice(1) = [1, 2, 3]
see? You should pass 1 to slice. That is the answer. Don't confuse the context with the arguments pseudo-array itself.

JavaScript filter callback that uses arguments

The goal is to filter an array and remove all occurrences of elements specified in its argument list.
For example, given removeElements([1, 2, 3, 1, 2, 3,4], 2, 3), my output should be [1,1,4].
function removeElements(arr) {
//I got an error that says **functions** not allowed **inside loop**
for(var i=1;i<arguments.length;i++){
arr= arr.filter(function(e){
return e!==arguments[i];
});
}
return arr;
}
Second thing I tried is moving the filter out of the for loop.
function removeElements(arr) {
function isNotEqual(e){
return e!==this;
}
for(var i=1;i<arguments.length;i++){
arr= arr.filter(isNotEqual,arguments[i]);
}
return arr;
}
None of them work. It always return arr as [1,2,3,1,2,3,4].
Can you please tell as to what is wrong in my usage? Or what is the approach for using filter in this scenario?
You can use Array.prototype.slice to get the blacklisted elements in array form.
Then use Array.prototype.indexOf to see if a given element is in the array for the filter function.
http://jsfiddle.net/Loothof7/
function removeElements(arr) {
var blacklist = Array.prototype.slice.call(arguments, 1);
return arr.filter(function(e) {
return blacklist.indexOf(e) == -1;
});
}
alert(removeElements([1, 2, 3, 1, 2, 3,4], 2, 3));
Note that Function.prototype.call is used on Array.prototype.slice with the this scope argument of arguments instead of directly calling arguments.slice since arguments isn't actually a "real" array.
To try to explain the reasons the snippets didn't succeed:
Every function defines its own arguments, even when the function is embedded.
function removeElements(arr) {
console.log(arguments);
// Arguments {
// 0: Array [1, 2, 3, 1, 2, 3, 4],
// 1: 2,
// 2: 3
// }
arr = arr.filter(function (e) {
console.log(arguments);
// Arguments {
// 0: 1, 2, 3, 1, ... (each value in `arr`)
// 1: 0, 1, 2, 3, ... (each index)
// 2: Array [1, 2, 3, 1, 2, 3, 4] (`arr` itself)
// }
// ...
});
return arr;
}
removeElements([1, 2, 3, 1, 2, 3, 4], 2, 3);
By retrieving values from arguments inside of the iterator (function(e) {...}), the statement will compare e against values in the 2nd Arguments.
for(var i=1;i<arguments.length;i++){
arr = arr.filter(function(e){
// 1st = 0 (the first index from `arr`)
// 2nd = [1, 2, 3, ...] (the `arr` itself)
console.log(arguments[i]);
return e!==arguments[i];
});
}
One option to resolve this is to access arguments outside of the iterator function, stashing the value in a variable that won't have the same conflict:
for(var i=1;i<arguments.length;i++){
var skip = arguments[i];
arr = arr.filter(function (e) {
return e !== skip;
});
}
http://jsfiddle.net/y7evq6nq/
If you're not using strict mode, the value of this will always be an Object.
When you provide a primitive value for a thisArg, it will be boxed into its equivalent Object type. In this case, a new Number.
function foo() {
console.log(typeof this, this); // 'object' Number(3)
return true;
}
[0].filter(foo, 3);
And, since === first checks for type equality, a primitive and boxed number cannot be equal:
var number = 3;
var boxedNumber = new Number(3);
console.log(typeof number); // 'number'
console.log(typeof boxedNumber); // 'object'
console.log(typeof number === typeof boxedNumber); // false
console.log(number === boxedNumber); // false
You can use the .valueOf() method to retrieve the primitive value from the object.
function isNotEqual(e){
return e!==this.valueOf();
}
http://jsfiddle.net/ow9b78bf/
Or, you can try using strict mode, which allows this to hold a primitive value without boxing it.
arguments are function specific pseudo-variable. Using it inside callback will give arguments of callback and not outer function.
function removeElements(arr) {
var args = Array.prototype.slice.call(arguments);
for(var i=1;i<args.length;i++){
arr= arr.filter(function(e){
return e!==args[i];
});
}
}
I have already answered this type of question here any way I post it to you
A simple function
function filter(){
var j = -1;
for(var i = 1; i < arguments.length; i++){
j = arguments[0].indexOf(arguments[i]);
if(j > -1){
arguments[0].splice(j, 1);
}
}
return arguments[0];
}
you can call this function with no of args eg:
filter([1,2,3,4,5,6,7,8,9], 1, 3, 5); //return [2,4,6,7,8,9]
filter([1,2,3,4,5,6,7,8,9], 1); //return [2,3,4,5,6,7,8,9]
This will use a callback function to check against the two numbers given in the arguments list.
function removeElements(arr, num1, num2){
return arr.filter(numChecks(num1, num2));
}
function numChecks(num1, num2){
return function(element){
return element !== num1 && element !== num2;
}
}
removeElements([1, 2, 3, 1, 2, 3,4], 2, 3)
I think this is what you want to do, maybe I'm wrong.
var result = [1, 2, 3, 1, 2, 3, 4].filter(function(item) {
return item !== 1 && item !== 2;
});
console.log(result); // [3, 3, 4]
UPDATE:
This function can do the job you want passing the items to be remove as an array instead of turning the arguments[1] param an Array using slice.call():
function removeItems(arr, items) {
return arr.filter(function (elem) {
return items.indexOf(elem) === -1;
});
}
var result = removeItems([1, 2, 3, 1, 2, 3, 4], [2, 3]);
console.log(result); // [1, 1, 4]
There are really good answers here, but you can do it very clean in this way, remember you have an objects option in filter method which you can use in the callback function, in this case i'm using it like : arguments[i] so I can check every value in the arguments array
function destroyer(arr) {
for(var i = 1; i < arguments.length; i++){
arr = arr.filter(isIn, arguments[i]);
}
function isIn(element,index, array){
if (element != this){
return element;
}
}
return arr;
}

Categories