In one of Douglas Crockford speeches, He favours the use of tail recursion over loops. this code was presented,
function repeat(myFunc) {
if (myFunc !== undefined) {
return repeat(myFunc);
}
}
I thought to define a myFunc but don't know if a static counter can retain its state during function calls or use a global counter. but being new to javascript I wanted to ask first. How can this be used in an example, say to count down from 10 to 0? Thanks.
You need to call the function myFunc somewhere -- and evaluate the result for further call of repeat.
function repeat(myFunc) {
if (myFunc()) {
repeat(myFunc);
}
}
var count = 10;
repeat(function () {
document.write(count + '<br>');
count--;
return count >= 0;
});
Here is a version that keeps state without global variable:
function repeat(myFunc, arg) {
if ((arg = myFunc(arg)) !== undefined) {
repeat(myFunc, arg);
}
}
repeat(function (count) {
document.write(count + ',');
count--;
if (count >= 0) return count;
}, 10);
How can this be used in an example, say to count down from 10 to 0?
Pass a Number to repeat, call repeat with decremented number as parameter until variable parameter is equal to 0
function repeat(n) {
console.log(n)
if (n) {
return repeat(--n);
}
}
repeat(10)
Not sure if I understand what approach you want, but you can use this to count down recursively
function repeat(myFunc, times) {
if(times > 0 && typeof myFunc == 'function') {
myFunc(times);
repeat(myFunc, times-1);
}
}
repeat(alert, 10);
Related
Learning some basic concepts in JavaScript "asynchronicity" from Frontendmasters course JavaScript: The Hard Parts, v2
I am given the exercise (Challenge 5):
Create a function limitedRepeat that console logs "hi for now" every second, but only for 5 seconds. Research how to use clearInterval() if you are not sure how to do this.
And following placeholder was given for this function:
function limitedRepeat() {
//CODE HERE
}
I was able to solve it as following (2 versions):
Version 1
function limitedRepeat() {
var totalLogs = 0;
var logFunc = setInterval(myTimer, 1000)
function myTimer() {
if(totalLogs < 5){
console.log("hi for now");
totalLogs++;
} else {
clearInterval(logFunc);
}
}
}
limitedRepeat(); // should log (every second, for 5 seconds): hi for now
Version 2
function limitedRepeat(totalLogs) {
console.log("hi for now");
var timery = setTimeout(timerable,1000);
function timerable() {
totalLogs++;
if(totalLogs >= 5){
clearTimeout(timery);
} else {
limitedRepeat(totalLogs);
}
}
}
limitedRepeat(0); // should log (every second, for 5 seconds): hi for now
Obviously, I have changed the signature of function in Version 2, so I am curious if there is solution that leverages setTimeout() and clearTimeout() and possibly recursion, that doesn't require signature of function to be changed - in other words for that recursive call set by timeout to somehow memorize how many times was the log printed to console?
With recursion;
function limitedRepeat(count = 0) {
if(count >= 5) return;
console.log('hi')
setTimeout(() => limitedRepeat(++count), 1000)
}
limitedRepeat()
Just make sure you increment before recalling the function.
This is my approach:
var count = 1,
timer = setInterval(limitedRepeat,1000)
function limitedRepeat() {
console.log('Hi for now');
count++;
if(count > 5) clearInterval(timer)
}
Using an inner named IIFE with recursion.
EDIT: We don't even need the closure to memoize the times executed if we pass the parameter to the inner function.
function limitedRepeat() {
const maxTimes = 5;
return (function _limitedRepeat(current) {
console.log("hi for now");
var timery = setTimeout(timerable, 1000);
function timerable() {
current++;
if (current >= maxTimes) {
return
}
_limitedRepeat(current);
}
})(0);
}
limitedRepeat();
I'm Using the code below to let my function A only be triggered 3 times.
as I'm new to Javascript I think maybe you guys could show me a better way.
var num = 0;
if(num<4){
function A() {
num++
}
}
I'd put the num check inside the function, in case you want to call it anywhere else it will check your num record when you call it instead of having it automatically run 3 times when you start your program.
var num = 0;
function A() {
if(num<4){
//perform whatever you want your func to do
num++;
} else {
console.log("You performed this function 3 times already");
}
}
This depends highly on what you want to achieve, but one way is using recursion:
function foo(param1, param2, count = 3) {
if (count > 0) {
// ... some code ...
return foo(param1, param2, count-1)
}
return null; // just as example and check for the null later
}
I want to count the number of times a function is called on click. I have this so far but its not quite working. Can anyone help with this?
function test(){
var count = (function({
var i = 0;
return function(){
return i += 1;
}
})();
if(count() == 2){
// do this
}
}
Invoke the function like so:
<select onclick="javascript: test();">
It looks like the count function isn't being invoked properly. How can I invoke the function and perform an operation on it? I want to perform logic at certain numbers of clicks.
var count = 0;
function test(){
count++;
if(count == 2){
// do this
console.log('do something');
}
}
<label onclick="javascript: test();">Test</label>
Take a variable and increment on number of click and do your operation.
You could use a closure to wrap the call.
function countUsage(methodToWrap, methodContext) {
const
wrapContext = methodContext || this;
let
count = 0;
// Return a method, this is the wrapped call.
return function methodWrapper() {
// Increase the counter by 1.
count++;
// Call the original method with the arguments.
methodToWrap.apply(wrapContext, arguments);
// Log the number of times the method was called.
console.log(`The method has been called ${count} times`);
}
}
function methodToWrap(text) {
console.log(`Log line ${text}`);
}
function sumToWrap(a, b) {
console.log(`Sum of ${a} + ${b} = ${a+b}`);
}
const
wrappedLog = countUsage(methodToWrap),
wrappedSum = countUsage(sumToWrap);
// For these three calls you will see the count is increased with each call.
wrappedLog('hello');
wrappedLog('how');
wrappedLog('are you');
// This will result in a log line that there has been 1 call as it is a different method.
wrappedSum(3, 4);
Does this work for you?
var i = 0;
function test(){
var count = (function() {
return function(){
return i += 1;
}
})();
if(count() == 2){
console.log('reached goal')
// do this
}
alert('current count' +i)
}
<select onclick="test()"><option>select</option></select>
And on your item just:
onlick="test()"
You generally had brackets in the wrong place etc and was always setting the value to 0.
I'm trying to develop a function that repeats a function x amount of times, just once, not based on settimerinterval or settimeout or anything based on time. I don't want to use a while/for loop directly, I want to use this repeat function.
I've tried something like this:
function repeat(func, times) {
for (x = 0; x < times; x++) {
eval(func)
}
}
But eval doesn't work on a function.
const func = () => console.log("hi");
const times = 3;
Array.from({length: times}, () => func());
I define a function.
I set the number of times to repeat function.
I make an array the size of times to repeat function.
I run the "defined function" on each element of the array.
Just call func and decrement counter and call the function repeat again.
function repeat(func, times) {
func();
times && --times && repeat(func, times);
}
repeat(function () { document.write('Hi<br>'); }, 5);
If Lodash is an option, then _.times
You can also define a reusable function, utilizing setInterval and clearInterval
function runFunctionXTimes(callback, interval, repeatTimes) {
let repeated = 0;
const intervalTask = setInterval(doTask, interval)
function doTask() {
if ( repeated < repeatTimes ) {
callback()
repeated += 1
} else {
clearInterval(intervalTask)
}
}
}
function sayHi() {
console.log("Hi")
}
The following line will run sayHi 5 times without wasting any time between completion of one and the beginning of another.
runFunctionXTimes(sayHi, 0, 5)
It is also possible to pass function arguments to setInerval, you can read more about it here https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval
Using recursion:
function repeat(fn, times) {
var loop = function (times) {
if (times) {
fn(times);
loop(--times);
}
}
loop(times);
}
repeat(function (times) {
console.log(times);
}, 5);
You may not be able to eval() a function, but you can call it. Here's the fix:
function repeat(func, times) {
for (x = 0; x < times; x++) {
func()
}
}
My broad question is what's the simplest way to differentiate between an initial and successive call to a recursive function in JavaScript.
Lemme give an example...
Let's say I want the following function to return false if the string passed to the function in the initial call is empty. Is there a way to do this without adding in another parameter to the function?
function isPalindrome(str) {
if (str.length <= 1) {
return true;
}
if (str.charAt(0) !== str.charAt(str.length -1)) {
return false;
}
return isPalindrome(str.substr(1, str.length - 2));
}
isPalindrome('') // returns true, but I want this to return false
Btw, I know the above function could be written more simply as:
function isPalindrome(str) {
return str == str.split('').reverse().join('');
}
But I'm reframing it as a recursive function to get at the broader question here...
Don't try to distinguish different calls - the result of the function should not depend on side effects and definitely not on the call stack.
Instead, use a second function that does a little different thing:
function isPalindrome(str) {
return str.length <= 1 ||
str.charAt(0) == str.charAt(str.length-1) && isPalindrome(str.slice(1, -1));
}
function isNonEmptyPalindrome(str) {
return str.length > 0 && isPalindrome(str);
}
You can have a nested function, even with the same name:
function isPalindrome(str) {
// The code out here is for the initial call:
if (str === '')
return false;
// Then do the recursive call
return function isPalindrome(str) {
// within this scope, isPalindrome refers to this function
if (str.length <= 1) {
return true;
}
if (str.charAt(0) !== str.charAt(str.length -1)) {
return false;
}
return isPalindrome(str.substr(1, str.length - 2));
}(str); // call this function immediately
}
For a general form:
function name(arg) {
// Setup code before initial call
//////////////////////////////
var retVal = function name(arg) {
// recursive routine code
}(arg);
// Cleanup code after recursion completes
/////////////////////////////////////////
return retVal;
}
Demo using factorial
This is essentially the same as Bergi's answer but with the helper function declared inside isPalindrome so that it isn't used elsewhere.
A better example for a palindrome is that all punctuation should be removed and letters made all upper or lower case (so that comparisons are not case sensitive), but only on the first call. After that, it's a simple matter of comparing characters.
The length == zero part is also only handled once, the function isn't called recursively if there are no characters left to compare.
The following does initial processing, then calls an inner function.
A function declaration is used instead of a named function expression as the latter have undesirable side effects in IE.
function isPalindrome(s) {
// Initial processing only on first call
// remove all punctuation
s = s.replace(/[^a-z0-9]/ig,'').toLowerCase();
return s.length == 0? false : doCheck(s);
function doCheck(s) {
if (s.length > 1) {
if (s.substr(0,1) == s.substr(s.length - 1, 1)) {
s = s.substr(1, s.length - 2);
return s.length? doCheck(s) : true;
} else {
return false;
}
}
return true;
}
}
console.log(isPalindrome("Madam I'm Adam")); // true
console.log(isPalindrome("Madam I'm Addam")); // false